Files
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

125 lines
9.6 KiB
Plaintext

---
title: 애플리케이션 구성
description: "`defineApplication`을 사용하여 앱의 식별 정보, 기본 역할, 변수 및 마켓플레이스 메타데이터를 선언합니다."
icon: rocket
---
모든 앱에는 `defineApplication` 호출이 정확히 하나 있어야 합니다. 다음 항목을 선언합니다:
* **식별 정보** — 범용 식별자, 표시 이름, 설명.
* **권한** — 로직 함수와 프런트 컴포넌트가 어떤 역할로 실행되는지.
* **변수** *(선택 사항)* — 코드에 환경 변수로 노출되는 키–값 쌍.
* **설치 전/설치 후/제거 훅** *(선택 사항)* — [Logic Functions](/l/ko/developers/extend/apps/logic/logic-functions)를 참조하세요.
```ts src/application-config.ts
import { defineApplication } from 'twenty-sdk/define';
export default defineApplication({
universalIdentifier: '39783023-bcac-41e3-b0d2-ff1944d8465d',
displayName: 'My Twenty App',
description: 'My first Twenty app',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
});
```
노트:
* `universalIdentifier` 필드는 여러분이 소유하는 변하지 않는 고유 ID입니다. 한 번만 생성하고 이후 동기화 동안에도 변하지 않도록 유지하세요.
* `applicationVariables`는 함수와 프런트 컴포넌트의 환경 변수가 됩니다. 로직 함수(서버 사이드)에서는 `process.env.VARIABLE_NAME`으로 사용할 수 있습니다. 프런트 컴포넌트에서는 `twenty-sdk/front-component`의 `getApplicationVariable('VARIABLE_NAME')`을 사용하세요. `isSecret: true`로 표시된 변수는 로직 함수에만 주입됩니다. 프런트 컴포넌트는 비밀이 아닌 변수만 받습니다.
* 기본 역할은 [`defineApplicationRole()`](/l/ko/developers/extend/apps/config/roles)로 표시된 역할 파일에서 자동으로 감지되므로, `defineApplication()`에서 이를 참조할 필요가 없습니다.
* 설치 전, 설치 후, 제거 함수는 매니페스트 빌드 중 자동으로 감지됩니다 — `defineApplication()`에서 별도로 참조할 필요가 없습니다.
* 하위 호환성을 위해 `defaultRoleUniversalIdentifier`를 명시적으로 전달하는 방식도 계속 지원되지만, 이제는 `defineApplicationRole()` 사용을 권장하며 이전 방식은 더 이상 권장되지 않습니다.
* `serverVariables`는 인스턴스 범위의 구성 및 비밀(예: API 키)입니다. `applicationVariables`와 달리, 매니페스트에는 값을 선언하지 않으며, 워크스페이스 운영자가 앱 설정에서 값을 채워 넣으면 설정된 이후에만 로직 함수에 주입됩니다.
* 두 종류의 변수 모두 `isDeprecated: true`를 허용합니다. 변수를 삭제하는 대신 사용 중지할 때 사용하십시오. 키 선언을 유지하면 저장된 값이 보존되고(삭제하면 운영자가 입력한 값이 사라집니다), 변수는 여전히 주입되므로 코드에서 `process.env.NEW_API_KEY ?? process.env.API_KEY`로 폴백할 수 있습니다. 더 이상 사용되지 않는 변수는 값이 없어지면 앱 설정에서 사라지며, 앱의 구성 검사에 절대 포함되지 않습니다. 따라서 `isDeprecated`가 `isRequired`보다 우선합니다.
* 앱의 **Settings** 탭에서 기본 변수 구성 섹션 대신 사용자 지정 구성 UI를 렌더링하려면, 별도의 파일에서 [`defineSettingsFrontComponent()`](/l/ko/developers/extend/apps/layout/front-components#custom-settings-component)를 사용하여 프론트 컴포넌트를 선언하세요. 앱당 하나만 허용됩니다. 시스템에서 관리하는 섹션(auto-upgrade, App URL, connections)은 항상 표시된 상태로 유지됩니다.
## 변수 유형
`applicationVariables`와 `serverVariables`는 모두 선택적인 `type`을 허용하며, `SELECT` / `MULTI_SELECT`의 경우 `options` 목록을 허용합니다. 지원되는 타입: `TEXT`(기본값), `BOOLEAN`, `NUMBER`, `NUMERIC`, `DATE`, `DATE_TIME`, `SELECT`, `MULTI_SELECT`, `ARRAY`, `RAW_JSON`, `RICH_TEXT`.
```ts src/application-config.ts
import { defineApplication, FieldType } from 'twenty-sdk/define';
export default defineApplication({
// ...identity, role...
applicationVariables: {
MAX_POSTCARDS: {
universalIdentifier: '5f4497e4-9030-4085-85eb-2c48b8d53713',
description: 'Maximum postcards per batch',
type: FieldType.NUMBER,
value: 10,
},
DEFAULT_REGION: {
universalIdentifier: '76c5c321-b6b6-46eb-b4fc-f9f04bb04227',
description: 'Default shipping region',
type: FieldType.SELECT,
options: [
{ label: 'Europe', value: 'eu' },
{ label: 'United States', value: 'us' },
],
value: 'eu',
},
},
});
```
`type`은 **표현과 검증**에만 영향을 줍니다. 즉, 워크스페이스 설정 UI에서 해당하는 입력 요소(토글, 숫자 필드, 드롭다운, 날짜 선택기, JSON 편집기 등)를 선택합니다. 또한 빌드가 구성(config)을 검증하도록 합니다(예를 들어 `SELECT` / `MULTI_SELECT`는 비어 있지 않은 `options`를 선언해야 합니다). 값이 코드로 전달되는 방식은 **변경되지 않습니다**.
값은 **항상 문자열로 주입**됩니다. 이는 환경 변수의 특성 때문입니다(`process.env.*`는 문자열만 허용). 로직 함수가 실행될 때, 실행기는 선언된 `type`에 따라 각 값을 직렬화하여 `process.env`를 구성하므로, 값이 어떻게 설정되었는지(매니페스트 기본값, 설정 UI, 또는 이전 버전)와 관계없이 문자열 형식이 일관되게 유지됩니다:
| 유형 | `process.env` 문자열 |
| ------------------------------------- | --------------------------------- |
| `TEXT`, `SELECT`, `DATE`, `DATE_TIME` | 원시 값(`"eu"`, `"2026-01-01"`) |
| `BOOLEAN` | `"true"` / `"false"` |
| `NUMBER`, `NUMERIC` | 10진수 문자열(`"10"`, `"2.5"`) |
| `MULTI_SELECT`, `ARRAY` | JSON 배열(`'["email","postcard"]'`) |
| `RAW_JSON`, `RICH_TEXT` | JSON 객체(`'{"retries":3}'`) |
문자열을 다시 기대하는 타입으로 파싱하세요:
```ts
const maxCards = Number(process.env.MAX_POSTCARDS); // "10" -> 10
const enabled = process.env.ENABLE_TRACKING === 'true'; // "true" -> true
const channels = JSON.parse(process.env.ENABLED_CHANNELS ?? '[]'); // '["email"]' -> ["email"]
const config = JSON.parse(process.env.PROVIDER_CONFIG ?? '{}'); // '{"retries":3}' -> { retries: 3 }
```
`getApplicationVariable('VARIABLE_NAME')`을 통해 값을 읽는 프런트 컴포넌트에도 동일하게 적용됩니다. 반환되는 값은 문자열이므로, 필요에 따라 파싱해야 합니다.
## 기본 함수 역할
[`defineApplicationRole()`](/l/ko/developers/extend/apps/config/roles)로 선언된 역할은 앱의 로직 함수와 프런트엔드 컴포넌트가 무엇에 접근할 수 있는지를 제어합니다:
* 로직 함수에 주입되는 런타임 토큰은 이 역할에서 파생됩니다. 사용자 권한으로 작동하는 호출은 해당 사용자가 할 수 있는 작업으로 더욱 제한되므로, 어느 쪽의 권한도 초과할 수 없습니다. [호출이 사용하는 액세스 권한](/l/ko/developers/extend/apps/logic/logic-functions#whose-access-a-call-uses)을 참조하세요.
* 타입드 API 클라이언트는 해당 역할에 부여된 권한으로 제한됩니다.
* 최소 권한 원칙을 따르세요: 함수에 필요한 권한만 선언하세요.
새 앱을 스캐폴딩하면 CLI가 `src/roles/default-role.ts`에 시작용 역할 파일을 생성합니다. 전체 내용은 [Roles & Permissions](/l/ko/developers/extend/apps/config/roles)를 참조하세요.
## 마켓플레이스 메타데이터
앱을 [게시](/l/ko/developers/extend/apps/operations/publishing)할 계획이라면, 다음 선택적 필드로 마켓플레이스에서 표시되는 방식을 제어합니다:
| 필드 | 설명 |
| ------------------ | ------------------------------------------------------------------- |
| `author` | 작성자 또는 회사 이름 |
| `category` | 마켓플레이스 필터링을 위한 앱 카테고리 |
| `logo` | `public/`에 번들된 앱 로고의 경로(예: `public/logo.png`) |
| `galleryImages` | `public/`에 번들된 갤러리 이미지 경로의 배열(예: `public/screenshot-1.png`) |
| `aboutDescription` | "About" 탭에 대한 더 긴 마크다운 설명. 생략하면 마켓플레이스는 npm의 패키지 `README.md`를 사용합니다 |
| `websiteUrl` | 웹사이트 링크 |
| `termsUrl` | 서비스 약관 링크 |
| `emailSupport` | 지원 이메일 주소 |
| `issueReportUrl` | 이슈 트래커 링크 |
<Note>
`logoUrl` 및 `screenshots`는 `logo` 및 `galleryImages`의 더 이상 사용되지 않는 별칭입니다. 다음 필드에는 외부 절대 URL(`http://` 또는 `https://`)이 지원되지 않습니다. 빌드 시 경고와 함께 제거됩니다. 대신 앱의 `public/` 폴더에 이미지를 번들하세요.
</Note>