> ## Documentation Index
> Fetch the complete documentation index at: https://twenty-claude-cool-pascal-5ay683.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 페이지 레이아웃

> `definePageLayout` 및 `definePageLayoutTab`을 사용하여 레코드 상세 페이지의 탭, 위젯, 그리고 프런트 컴포넌트가 렌더링되는 위치를 사용자 지정합니다.

**페이지 레이아웃**은 레코드의 상세 페이지가 어떻게 구성되는지를 제어합니다. 어떤 탭이 표시되고, 그 탭 안에 어떤 위젯이 포함되는지를 결정합니다. 소유한 객체에 대한 레이아웃을 선언하려면 `definePageLayout()`을 사용하고, 이미 존재하는 레이아웃(사용자 소유 또는 표준 Twenty 레이아웃)에 단일 탭을 추가하려면 `definePageLayoutTab()`을 사용합니다.

| 사용 사례                                                | 엔터티                   |
| ---------------------------------------------------- | --------------------- |
| 소유한 객체의 레코드 페이지에 대한 전체 레이아웃을 정의합니다.                  | `definePageLayout`    |
| 기존 레이아웃(자신이 소유한 객체의 레이아웃이든, 표준 레이아웃이든)에 탭 하나를 추가합니다. | `definePageLayoutTab` |

## definePageLayout

전체 상세 페이지를 소유할 때 사용합니다. 일반적으로 직접 정의한 사용자 지정 객체에 해당합니다.

```ts src/page-layouts/example-record-page-layout.ts theme={null}
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';

export default definePageLayout({
  universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
  name: 'Example Record Page',
  type: 'RECORD_PAGE',
  objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
  tabs: [
    {
      universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5',
      title: 'Hello World',
      position: 50,
      icon: 'IconWorld',
      layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
      widgets: [
        {
          universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d',
          title: 'Hello World',
          type: 'FRONT_COMPONENT',
          configuration: {
            configurationType: 'FRONT_COMPONENT',
            frontComponentUniversalIdentifier:
              HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
          },
        },
      ],
    },
  ],
});
```

### 핵심 요점

* `type`은 `'RECORD_INDEX'`, `'RECORD_PAGE'`, `'DASHBOARD'`, `'STANDALONE_PAGE'` 중 하나입니다. 'RECORD\_PAGE'를 사용하면 특정 객체의 상세 보기를 사용자 지정할 수 있습니다.
* `objectUniversalIdentifier`는 이 레이아웃이 적용되는 객체를 지정합니다.
* 각 `tab`은 `title`, `position`, `layoutMode`를 사용해 페이지의 섹션을 정의합니다. 레코드 페이지와 독립형 페이지에는 `VERTICAL_LIST`를, 대시보드에는 `GRID`를, 탭 뷰포트를 채워야 하는 단일 위젯에는 `CANVAS`를 사용합니다. `VERTICAL_LIST` 탭은 위젯을 세로로 쌓습니다. 타임라인, 파일, 메모, 작업, 워크플로와 같이 자체 스크롤을 관리하는 기본 제공 위젯은 하나의 뷰포트를 채우며, 필드, 프론트 컴포넌트, 그래프 및 기타 콘텐츠 크기에 맞는 위젯은 콘텐츠 높이 또는 구성된 높이로 렌더링됩니다. `GRID` 탭은 항상 12열 그리드에서 위젯을 카드로 배치합니다. `CANVAS` 위젯에는 명시적 위치가 없습니다. 캔버스 탭에 여러 위젯이 포함된 경우, 뷰포트를 채우는 대신 콘텐츠 높이로 렌더링됩니다.
* `layoutMode`를 명시적으로 설정하세요. 이를 생략하면 `STANDALONE_PAGE`에서는 `VERTICAL_LIST`가, 그 외의 경우에는 `GRID`가 기본값으로 설정되는데, 이는 레코드 페이지에서는 거의 원하지 않는 동작입니다.
* 탭 내부의 각 `widget`은 [front component](/l/ko/developers/extend/apps/layout/front-components), 관계 목록 또는 기타 기본 제공 위젯 타입을 렌더링할 수 있습니다.
* `FRONT_COMPONENT` 위젯은 동일한 앱의 명령 메뉴 항목 범용 식별자로 이루어진 순서 있는 배열로 `headerCommandMenuItemUniversalIdentifiers`를 설정할 수 있습니다. 이러한 작업은 위젯 카드 헤더에 아이콘 버튼으로 표시되며, 명령 수준의 가용성 및 권한 검사를 유지합니다. 식별자는 고유해야 하며 앱이 설치될 때 확인 가능해야 합니다.
* 탭의 `position`은 탭의 순서를 제어합니다. 값을 더 높게 설정하면(예: 50) 기본 탭 뒤에 사용자 지정 탭을 배치할 수 있습니다.

### 필드 위젯

`FIELD` 위젯은 레코드의 하나의 필드를 렌더링합니다. 관계 필드의 경우 관련 레코드 목록을 포함할 수도 있습니다:

```ts theme={null}
{
  universalIdentifier: 'c1c2c3c4-c5c6-4000-8000-000000000003',
  title: 'People → Opportunities',
  type: 'FIELD',
  configuration: {
    configurationType: 'FIELD',
    fieldMetadataId: PEOPLE_FIELD_UNIVERSAL_IDENTIFIER,
    fieldDisplayMode: 'TABLE',
    nestedRelationFieldMetadataId: OPPORTUNITIES_FIELD_UNIVERSAL_IDENTIFIER,
  },
}
```

* `fieldMetadataId`는 레이아웃의 오브젝트에 있는 필드의 범용 식별자를 받습니다.
* `fieldDisplayMode`는 `'FIELD'`, `'CARD'`, `'EDITOR'`, `'VIEW'`, `'TABLE'` 중 하나입니다. `TABLE`은 일대다 관계 필드의 레코드를 나열하는 뷰를 포함합니다.
* `nestedRelationFieldMetadataId`는 선택 사항이며, 관계 대상 객체에 있는 일대다 관계 필드의 범용 식별자를 받습니다. 이를 통해 두 번의 관계 홉을 거친 레코드를 나열할 수 있습니다(예: Company 페이지에서 해당 회사 사람들의 Opportunity 레코드를 나열하거나, Person 페이지에서 해당 사람의 회사의 Opportunity 레코드를 나열하는 경우). 첫 번째 홉은 일대다 또는 다대일 관계 필드일 수 있지만, 두 번째 홉은 반드시 일대다 관계여야 합니다(junction 관계는 지원되지 않습니다). 또한 `fieldDisplayMode: 'TABLE'`이 필요하며, 다른 어떤 display mode와 결합하면 유효성 검사 오류가 발생합니다. 중첩 위젯은 항상 임베디드 뷰로 렌더링되기 때문입니다.

## definePageLayoutTab

기존 레이아웃에 탭만 추가하려는 경우에 사용합니다. 예를 들어, 표준 Company 페이지에 분석 탭을 추가하거나, 자신이 소유한 객체의 레이아웃에 AI 요약 탭을 연결하는 경우입니다.

```ts src/page-layouts/example-extra-tab.ts theme={null}
import {
  definePageLayoutTab,
  PageLayoutTabLayoutMode,
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';

export default definePageLayoutTab({
  universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
  pageLayoutUniversalIdentifier:
    STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
      .universalIdentifier,
  title: 'Hello World',
  position: 1000,
  icon: 'IconWorld',
  layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
  widgets: [
    {
      universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002',
      title: 'Hello World',
      type: 'FRONT_COMPONENT',
      configuration: {
        configurationType: 'FRONT_COMPONENT',
        frontComponentUniversalIdentifier:
          HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
      },
    },
  ],
});
```

### 핵심 요점

* `pageLayoutUniversalIdentifier`는 **필수**이며, 설치 시점에 이미 존재하는 페이지 레이아웃을 가리켜야 합니다. 이는 표준 Twenty 레이아웃이거나, 사용자의 앱에서 정의한 레이아웃일 수 있습니다. 현재는 설치된 다른 앱이 소유한 레이아웃에 대한 크로스 앱 참조는 지원되지 않습니다. 상위 레이아웃이 없으면, 명확한 유효성 검사 오류와 함께 설치가 실패합니다.

* 표준 Twenty 레이아웃의 경우 `twenty-sdk/define`에서 식별자를 가져옵니다:

  ```ts theme={null}
  import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';

  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
  // STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
  // …
  ```

  각 레이아웃 항목은 `tabs`와 해당 `widgets`도 노출하므로, 모든 수준을 참조할 수 있습니다:

  ```ts theme={null}
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home
    .universalIdentifier;
  STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets
    .fields.universalIdentifier;
  ```

  짧은 별칭인 `STANDARD_PAGE_LAYOUT`도 사용할 수 있습니다:

  ```ts theme={null}
  import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';

  STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
  ```

* `widgets`는 이 탭에만 범위가 국한됩니다. `definePageLayout`에 인라인으로 정의된 위젯과 완전히 동일한 방식으로 [front components](/l/ko/developers/extend/apps/layout/front-components), 뷰 등을 참조합니다.

* `position`은 대상 레이아웃의 기존 탭과의 순서를 제어합니다. 기본 제공 탭을 기준으로 원하는 위치에 탭이 놓이도록 값을 선택하세요.

* 기존 레이아웃에만 추가하려는 경우에는 `definePageLayout` 대신 이것을 사용하세요. 전체 레이아웃을 소유하는 경우에는 `definePageLayout`을 사용하세요.
