--- title: 페이지 레이아웃 description: "`definePageLayout` 및 `definePageLayoutTab`을 사용하여 레코드 상세 페이지의 탭, 위젯, 그리고 프런트 컴포넌트가 렌더링되는 위치를 사용자 지정합니다." icon: table-columns --- **페이지 레이아웃**은 레코드의 상세 페이지가 어떻게 구성되는지를 제어합니다. 어떤 탭이 표시되고, 그 탭 안에 어떤 위젯이 포함되는지를 결정합니다. 소유한 객체에 대한 레이아웃을 선언하려면 `definePageLayout()`을 사용하고, 이미 존재하는 레이아웃(사용자 소유 또는 표준 Twenty 레이아웃)에 단일 탭을 추가하려면 `definePageLayoutTab()`을 사용합니다. | 사용 사례 | 엔터티 | | ---------------------------------------------------- | --------------------- | | 소유한 객체의 레코드 페이지에 대한 전체 레이아웃을 정의합니다. | `definePageLayout` | | 기존 레이아웃(자신이 소유한 객체의 레이아웃이든, 표준 레이아웃이든)에 탭 하나를 추가합니다. | `definePageLayoutTab` | ## definePageLayout 전체 상세 페이지를 소유할 때 사용합니다. 일반적으로 직접 정의한 사용자 지정 객체에 해당합니다. ```ts src/page-layouts/example-record-page-layout.ts 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 { 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 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 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 STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home .universalIdentifier; STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets .fields.universalIdentifier; ``` 짧은 별칭인 `STANDARD_PAGE_LAYOUT`도 사용할 수 있습니다: ```ts 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`을 사용하세요.