i18n - docs translations (#21789)

Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-06-18 15:21:04 +02:00
committed by GitHub
parent 22baf2c6c5
commit 2b3b2362db
704 changed files with 38268 additions and 9002 deletions
@@ -0,0 +1,50 @@
---
title: 객체 확장하기
description: 표준 Twenty 객체(Person, Company 등)에 필드를 추가합니다. 또는 `defineField`를 사용해 다른 앱의 객체에 필드를 추가합니다.
icon: wand-magic-sparkles
---
`defineField()`를 사용해 소유하지 않은 객체(예: Person이나 Company 같은 표준 Twenty 객체 또는 다른 설치된 앱에서 제공하는 객체)에 필드를 추가합니다. [`defineObject`](/l/ko/developers/extend/apps/data/objects) 안에 선언된 인라인 필드와 달리, 독립형 필드는 어느 객체를 확장할지 지정하기 위해 `objectUniversalIdentifier`가 필요합니다.
```ts src/fields/company-loyalty-tier.field.ts
import { defineField, FieldType } from 'twenty-sdk/define';
export default defineField({
universalIdentifier: 'f2a1b3c4-d5e6-7890-abcd-ef1234567890',
objectUniversalIdentifier: '701aecb9-eb1c-4d84-9d94-b954b231b64b', // Company object
name: 'loyaltyTier',
type: FieldType.SELECT,
label: 'Loyalty Tier',
icon: 'IconStar',
options: [
{ value: 'BRONZE', label: 'Bronze', position: 0, color: 'orange' },
{ value: 'SILVER', label: 'Silver', position: 1, color: 'gray' },
{ value: 'GOLD', label: 'Gold', position: 2, color: 'yellow' },
],
});
```
## 핵심 요점
* `objectUniversalIdentifier`는 대상 객체를 식별합니다. 표준 Twenty 객체의 경우 `twenty-sdk`에서 상수를 가져옵니다:
```ts
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier
// …
```
* `defineObject()`에서 필드를 **인라인으로 정의할 때는**, `objectUniversalIdentifier`가 **필요하지 않습니다** — 상위 객체에서 상속됩니다.
* `defineField()`는 `defineObject()`로 생성하지 않은 객체에 필드를 추가하는 유일한 방법입니다.
* 파일 위치는 자유롭게 정할 수 있습니다. 규칙으로는 `src/fields/\<name>.field.ts`를 사용하지만, SDK는 `src/` 어디에서든 필드를 감지합니다.
* 표준 페이지 레이아웃(예: Task 또는 Company 상세 페이지)에 탭을 추가하려면, `twenty-sdk/define`의 `STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS`와 함께 [`definePageLayoutTab`](/l/ko/developers/extend/apps/layout/page-layouts#definepagelayouttab)을(를) 사용하십시오.
## 기존 객체에 관계 추가하기
관계 필드(예: 사용자 정의 객체를 표준 `Person`에 연결)를 추가하려면, `FieldType.RELATION`과 함께 `defineField()`를 사용하세요. 패턴은 인라인 관계와 동일하지만, `objectUniversalIdentifier`를 명시적으로 설정합니다. 양방향 패턴은 [Relations](/l/ko/developers/extend/apps/data/relations)를 참조하세요.
@@ -0,0 +1,104 @@
---
title: 개체
description: defineObject를 사용하여 고유한 필드를 가진 사용자 정의 테이블인 새 레코드 유형을 선언합니다.
icon: 테이블
---
사용자 정의 **개체**는 워크스페이스에 앱이 추가하는 새로운 레코드 유형입니다. 엽서(Post Card), 송장(Invoice), 구독(Subscription) 등 도메인에 특화된 어떤 것이라도 될 수 있습니다. 각 개체는 스키마(필드, 관계, 기본값)와 동기화 및 배포 간에도 유지되는 안정적인 범용 식별자를 선언합니다.
```ts src/objects/post-card.object.ts
import { defineObject, FieldType } from 'twenty-sdk/define';
enum PostCardStatus {
DRAFT = 'DRAFT',
SENT = 'SENT',
DELIVERED = 'DELIVERED',
RETURNED = 'RETURNED',
}
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
fields: [
{
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
name: 'content',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
},
{
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
name: 'recipientName',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
},
{
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
name: 'recipientAddress',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
},
{
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
name: 'status',
type: FieldType.SELECT,
label: 'Status',
icon: 'IconSend',
defaultValue: `'${PostCardStatus.DRAFT}'`,
options: [
{ value: PostCardStatus.DRAFT, label: 'Draft', position: 0, color: 'gray' },
{ value: PostCardStatus.SENT, label: 'Sent', position: 1, color: 'orange' },
{ value: PostCardStatus.DELIVERED, label: 'Delivered', position: 2, color: 'green' },
{ value: PostCardStatus.RETURNED, label: 'Returned', position: 3, color: 'orange' },
],
},
{
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
name: 'deliveredAt',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
},
],
});
```
## 핵심 요점
* `universalIdentifier`는 배포 전반에서 고유하고 안정적이어야 합니다.
* 각 필드는 `name`, `type`, `label` 및 고유하고 안정적인 `universalIdentifier`가 필요합니다.
* `fields` 배열은 선택 사항입니다. 사용자 정의 필드 없이도 개체를 정의할 수 있습니다.
* 여기에서 정의된 인라인 필드는 `objectUniversalIdentifier`가 **필요하지 않습니다** — 상위 개체에서 상속됩니다. 소유하지 않은 개체에 필드를 추가하려면 [`defineField()`](/l/ko/developers/extend/apps/data/extending-objects)를 사용하세요.
* `yarn twenty dev:add object`를 사용하여 새 개체를 스캐폴딩할 수 있으며, 이름, 필드, 관계 설정 과정을 안내합니다. [Architecture → Scaffolding entities](/l/ko/developers/extend/apps/getting-started/scaffolding)를 참조하세요.
<Note>
**기본 필드는 자동으로 추가됩니다.** 사용자 정의 개체를 정의하면 Twenty가 `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, `deletedAt`와 같은 표준 필드를 자동으로 생성합니다. 이 필드들은 `fields` 배열에 선언할 필요가 없습니다 — 사용자 정의 필드만 선언하면 됩니다. 동일한 이름으로 필드를 선언하여 기본 필드를 재정의할 수 있지만, 이는 거의 바람직하지 않습니다.
</Note>
## 기본값
리터럴 문자열 기본값은 문자열 **내부에서** 작은따옴표로 감싸야 합니다. 즉, `defaultValue: "'Draft'"`처럼 작성해야 하며, `defaultValue: "Draft"`처럼 작성하면 안 됩니다. 그래서 위의 `status` 필드는 `` `'${PostCardStatus.DRAFT}'` ``를 사용합니다.
따옴표로 감싸지 않은 문자열은 레코드가 생성될 때 평가되는 계산형 기본값으로 예약되어 있습니다.
* `'uuid'` — UUID를 생성합니다 (`UUID` 필드용).
* `'now'` — 현재 타임스탬프입니다 (`DATE_TIME` 필드용).
동일한 규칙이 복합 기본값의 문자열 하위 필드(예: `ACTOR` 필드의 `{ source: "'MANUAL'" }`)와 `SELECT`/`MULTI_SELECT` 값에도 적용됩니다. 따옴표로 감싸지 않은 리터럴 문자열 기본값은 앱을 빌드할 때 경고를 발생시킵니다.
## 다음 단계
* **이 개체를 다른 개체와 연결** — 양방향 관계 패턴은 [Relations](/l/ko/developers/extend/apps/data/relations)를 참조하세요.
* **다른 앱의 개체에 필드 추가** — `defineField()`에 대해서는 [Extending Objects](/l/ko/developers/extend/apps/data/extending-objects)를 참조하세요.
* **UI에 이 개체 표시** — 사이드바에 배치하려면 [Views](/l/ko/developers/extend/apps/layout/views) 및 [Navigation Menu Items](/l/ko/developers/extend/apps/layout/navigation-menu-items)를 참조하세요.
@@ -0,0 +1,97 @@
---
title: 개요
description: 워크스페이스에 앱이 추가하는 데이터(개체, 필드, 관계)의 구성을 정의하세요.
icon: database
---
Twenty 앱의 **데이터 레이어**는 워크스페이스에 앱이 *추가하는* 데이터입니다. 앱이 선언하는 새로운 레코드 타입, 기존 개체에 추가하는 열, 그리고 이러한 레코드들이 서로 연결되는 방식이 여기에 포함됩니다.
```text
┌──────────────────────────────────────────────────┐
│ Object — a record type, e.g. PostCard │
│ ├─ Field (name, type, label) │
│ ├─ Field │
│ └─ Relation (link to another object) │
└──────────────────────────────────────────────────┘
├── lives in your app, OR
┌──────────────────────────────────────────────────┐
│ Standard / other apps' objects │
│ └─ Field added by your app via defineField │
└──────────────────────────────────────────────────┘
```
## 이 섹션에서 다루는 내용
<CardGroup cols={2}>
<Card title="개체" icon="table" href="/l/ko/developers/extend/apps/data/objects">
`defineObject` — 자체 필드를 가진 새로운 레코드 타입을 선언합니다.
</Card>
<Card title="객체 확장하기" icon="wand-magic-sparkles" href="/l/ko/developers/extend/apps/data/extending-objects">
`defineField` — 표준 개체 또는 다른 앱의 개체에 필드를 추가합니다.
</Card>
<Card title="관계" icon="diagram-project" href="/l/ko/developers/extend/apps/data/relations">
개체 간의 양방향 `MANY_TO_ONE` / `ONE_TO_MANY` 연결입니다.
</Card>
</CardGroup>
## 엔터티 한눈에 보기
| 엔터티 | 목적 | 정의 방식 |
| ------ | --------------------------------------------------------------------------- | ------------------------------------------ |
| **객체** | 고유한 필드를 가진 새로운 사용자 정의 레코드 타입(예: PostCard, Invoice) | `defineObject()` |
| **필드** | 객체의 열 독립형 필드를 사용하면 직접 생성하지 않은 객체도 확장할 수 있습니다(예: Company에 `loyaltyTier` 추가). | `defineField()` |
| **관계** | 두 객체 사이의 양방향 연결 — 양쪽 모두 필드로 선언됩니다. | `FieldType.RELATION`을 사용하는 `defineField()` |
| **색인** | 객체 중 하나에 대해 반복적으로 실행되는 쿼리를 빠르게 하기 위한 데이터베이스 색인 | `defineIndex()` |
SDK는 빌드 시 AST 분석을 통해 이를 감지하므로, 파일 구성은 전적으로 여러분에게 달려 있습니다. 권장 컨벤션은 `src/objects/`, `src/fields/`, `src/indexes/`입니다. 안정적인 `universalIdentifier` UUID가 배포 간 모든 것을 하나로 연결합니다.
## 색인(선택 사항)
앱은 반복적인 쿼리를 빠르게 유지하기 위해 객체와 함께 색인을 포함하여 배포할 수 있습니다. 가장 일반적인 사례는 자주 읽는 상태 열 또는 외래 키 열입니다.
```ts src/indexes/post-card-status.index.ts
import { defineIndex } from 'twenty-sdk/define';
import {
POST_CARD_UNIVERSAL_IDENTIFIER,
STATUS_FIELD_UNIVERSAL_IDENTIFIER,
} from '../objects/post-card.object';
export default defineIndex({
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff0',
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
fields: [
{
universalIdentifier: 'b6e9d2a1-5a4c-46ca-9d52-42c8f02d1ff1',
fieldUniversalIdentifier: STATUS_FIELD_UNIVERSAL_IDENTIFIER,
},
],
});
```
### 고유 색인
`defineIndex`는 단일 열 및 다중 열 고유성 모두에 대해 `isUnique: true`를 허용합니다. 이것이 권장 기본 요소이며, `defineField({ isUnique: true })`는 사용이 중단(deprecated)되었고 향후 릴리스에서 제거될 예정입니다.
```ts
defineIndex({
universalIdentifier: '…',
objectUniversalIdentifier: PERSON_UNIVERSAL_IDENTIFIER,
isUnique: true,
fields: [{ universalIdentifier: '…', fieldUniversalIdentifier: EMAIL_FIELD_UNIVERSAL_IDENTIFIER }],
});
```
### 기타 제약 조건
* 부분 `WHERE` 절은 관리자 제어 하에 유지되며, 앱에서 이를 선언할 수 없습니다.
* 각 객체에는 사용자 정의 색인이 최대 10개까지 허용됩니다(프레임워크 자체 색인은 포함되지 않음).
`fields` 배열의 순서는 Postgres가 사용하는 순서대로 지정하세요. 전화번호부처럼 가장 왼쪽 열이 먼저 오도록 합니다. 색인은 공짜가 아닙니다. 테이블에 대한 모든 쓰기 작업은 색인을 갱신합니다. 해당 색인을 필요로 하는 쿼리가 있을 때에만 색인을 추가하세요.
<Note>
**Application Config** 또는 **Roles & Permissions**를 찾고 계신가요? 이러한 항목은 앱이 추가하는 데이터가 아니라 앱 자체를 설명하며, [Config](/l/ko/developers/extend/apps/config/overview) 아래에 있습니다. **Connections**(Linear, GitHub, Slack OAuth)를 찾고 계신가요? 이들은 로직 함수 *내부에서* 호출되기 위한 것이며, [Logic](/l/ko/developers/extend/apps/logic/connections) 아래에 있습니다.
</Note>
@@ -0,0 +1,160 @@
---
title: 관계
description: 양방향 MANY_TO_ONE / ONE_TO_MANY 관계로 객체들을 서로 연결합니다.
icon: diagram-project
---
관계는 두 객체를 서로 연결합니다. Twenty에서는 관계가 항상 **양방향**입니다 — 모든 관계에는 두 개의 측면이 있으며, 각 측면은 서로를 참조하는 필드로 선언됩니다.
| 관계 유형 | 설명 | 외래 키가 있나요? |
| ------------- | ------------------------------- | -------------------- |
| `MANY_TO_ONE` | 이 객체의 여러 레코드가 대상의 한 레코드를 가리킵니다 | 예 (`joinColumnName`) |
| `ONE_TO_MANY` | 이 객체의 하나의 레코드가 대상의 여러 레코드를 가집니다 | 아니오 (역방향) |
## 관계 작동 방식
모든 관계에는 서로를 참조하는 **두 개의 필드**가 필요합니다:
1. **MANY_TO_ONE** 측 — 외래 키를 보유한 객체에 존재합니다.
2. **ONE_TO_MANY** 측 — 컬렉션을 소유한 객체에 존재합니다.
두 필드 모두 `FieldType.RELATION`을 사용하며 `relationTargetFieldMetadataUniversalIdentifier`를 통해 서로를 교차 참조합니다.
## 예시: Post Card에는 여러 수신자가 있습니다
`PostCard`는 여러 `PostCardRecipient` 레코드에 전송될 수 있습니다. 각 수신자는 정확히 하나의 Post Card에 속합니다.
**1단계: PostCard에서 ONE_TO_MANY 측을 정의합니다** ("one" 측):
```ts src/fields/post-card-recipients-on-post-card.field.ts
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
// Export so the other side can reference it
export const POST_CARD_RECIPIENTS_FIELD_ID = 'a1111111-1111-1111-1111-111111111111';
// Import from the other side
import { POST_CARD_FIELD_ID } from './post-card-on-post-card-recipient.field';
export default defineField({
universalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
objectUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'postCardRecipients',
label: 'Post Card Recipients',
icon: 'IconUsers',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_FIELD_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});
```
**2단계: PostCardRecipient에서 MANY_TO_ONE 측을 정의합니다** ("many" 측 — 외래 키를 보유함):
```ts src/fields/post-card-on-post-card-recipient.field.ts
import { defineField, FieldType, RelationType, OnDeleteAction } from 'twenty-sdk/define';
import { POST_CARD_UNIVERSAL_IDENTIFIER } from '../objects/post-card.object';
import { POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER } from '../objects/post-card-recipient.object';
// Export so the other side can reference it
export const POST_CARD_FIELD_ID = 'b2222222-2222-2222-2222-222222222222';
// Import from the other side
import { POST_CARD_RECIPIENTS_FIELD_ID } from './post-card-recipients-on-post-card.field';
export default defineField({
universalIdentifier: POST_CARD_FIELD_ID,
objectUniversalIdentifier: POST_CARD_RECIPIENT_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'postCard',
label: 'Post Card',
icon: 'IconMail',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.CASCADE,
joinColumnName: 'postCardId',
},
});
```
<Note>
**순환 임포트:** 두 관계 필드는 서로의 `universalIdentifier`를 참조합니다. 순환 임포트 문제를 피하려면, 각 파일에서 필드 ID를 명명된 상수로 export하고 다른 파일에서 import하세요. 빌드 시스템은 컴파일 시점에 이를 해결합니다.
</Note>
## 표준 객체와의 관계 설정
내장 Twenty 객체(Person, Company 등)와 관계를 만들려면 `STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS`를 사용하세요:
```ts src/fields/person-on-self-hosting-user.field.ts
import {
defineField,
FieldType,
RelationType,
OnDeleteAction,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER } from '../objects/self-hosting-user.object';
export const PERSON_FIELD_ID = 'c3333333-3333-3333-3333-333333333333';
export const SELF_HOSTING_USER_REVERSE_FIELD_ID = 'd4444444-4444-4444-4444-444444444444';
export default defineField({
universalIdentifier: PERSON_FIELD_ID,
objectUniversalIdentifier: SELF_HOSTING_USER_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'person',
label: 'Person',
description: 'Person matching with the self hosting user',
isNullable: true,
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: SELF_HOSTING_USER_REVERSE_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'personId',
},
});
```
## 관계 필드 속성
| 속성 | 필수 | 설명 |
| ------------------------------------------------- | ---------------- | --------------------------------------------------------------------- |
| `type` | 예 | `FieldType.RELATION`이어야 함 |
| `relationTargetObjectMetadataUniversalIdentifier` | 예 | 대상 객체의 `universalIdentifier` |
| `relationTargetFieldMetadataUniversalIdentifier` | 예 | 대상 객체의 일치하는 필드의 `universalIdentifier` |
| `universalSettings.relationType` | 예 | `RelationType.MANY_TO_ONE` 또는 `RelationType.ONE_TO_MANY` |
| `universalSettings.onDelete` | MANY_TO_ONE에만 해당 | 참조된 레코드가 삭제될 때의 동작: `CASCADE`, `SET_NULL`, `RESTRICT`, 또는 `NO_ACTION` |
| `universalSettings.joinColumnName` | MANY_TO_ONE에만 해당 | 외래 키에 대한 데이터베이스 컬럼 이름(예: `postCardId`) |
## 인라인 관계 필드
[`defineObject`](/l/ko/developers/extend/apps/data/objects) 안에서 관계를 직접 선언할 수도 있습니다. 인라인인 경우에는 `objectUniversalIdentifier`를 생략하세요 — 상위 객체에서 상속됩니다:
```ts
export default defineObject({
universalIdentifier: '...',
nameSingular: 'postCardRecipient',
// ...
fields: [
{
universalIdentifier: POST_CARD_FIELD_ID,
type: FieldType.RELATION,
name: 'postCard',
label: 'Post Card',
relationTargetObjectMetadataUniversalIdentifier: POST_CARD_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: POST_CARD_RECIPIENTS_FIELD_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.CASCADE,
joinColumnName: 'postCardId',
},
},
// … other fields
],
});
```