앱이란 무엇인가요?
앱을 사용하면 Twenty 맞춤설정을 코드로 구축하고 관리할 수 있습니다. 모든 것을 UI에서 구성하는 대신, 데이터 모델과 로직 함수를 코드로 정의합니다 — 이를 통해 더 빠르게 구축·유지 관리하고 여러 워크스페이스에 배포할 수 있습니다. 현재 가능한 작업:- 사용자 정의 객체와 필드를 코드로 정의하기(관리형 데이터 모델)
- 사용자 정의 트리거로 로직 함수 구축
- 동일한 앱을 여러 워크스페이스에 배포
- 사용자 정의 UI 레이아웃 및 컴포넌트
사전 준비
- Node.js 24+ 및 Yarn 4
- Twenty 워크스페이스와 API 키(https://app.twenty.com/settings/api-webhooks에서 생성)
시작하기
공식 스캐폴더를 사용해 새 앱을 만든 다음, 인증하고 개발을 시작하세요:프로젝트 구조(스캐폴딩됨)
npx create-twenty-app@latest my-twenty-app를 실행하면, 스캐폴더가 다음을 수행합니다:
my-twenty-app/에 최소한의 기본 애플리케이션을 복사합니다- 로컬
twenty-sdk종속성과 Yarn 4 구성을 추가합니다 twentyCLI와 연결된 설정 파일과 스크립트를 생성합니다- 기본 애플리케이션 구성과 기본 함수 역할을 생성합니다
설정보다 관례
애플리케이션은 파일 접미사로 엔티티를 감지하는 관례 우선 접근 방식을 사용합니다. 이를 통해src/app/ 폴더 내에서 유연하게 구성할 수 있습니다:
| 파일 접미사 | 엔티티 유형 |
|---|---|
*.object.ts | 사용자 정의 객체 정의 |
*.function.ts | 서버리스 함수 정의 |
*.front-component.tsx | 프런트 컴포넌트 정의 |
*.role.ts | 역할 정의 |
지원되는 폴더 구성 방식
엔티티를 다음 패턴 중 어느 것으로든 구성할 수 있습니다: 전통적(유형별):- package.json: 앱 이름, 버전, 엔진(Node 24+, Yarn 4)을 선언하고,
twenty-sdk와 함께app:dev,app:generate,entity:add,function:logs,function:execute,app:uninstall,auth:login같은 스크립트를 추가합니다. 이 스크립트들은 로컬twentyCLI에 위임됩니다. - .gitignore:
node_modules,.yarn,generated/(타입드 클라이언트),dist/,build/, 커버리지 폴더, 로그 파일,.env*파일 등의 일반 산출물을 무시합니다. - yarn.lock, .yarnrc.yml, .yarn/: 프로젝트에서 사용하는 Yarn 4 툴체인을 고정하고 구성합니다.
- .nvmrc: 프로젝트에서 예상하는 Node.js 버전을 고정합니다.
- eslint.config.mjs 및 tsconfig.json: 앱의 TypeScript 소스에 대한 린팅 및 TypeScript 구성을 제공합니다.
- README.md: 앱 루트에 기본 안내를 담은 간단한 README입니다.
- public/: 애플리케이션과 함께 제공되는 공개 자산(이미지, 폰트, 정적 파일)을 저장하는 폴더입니다. 여기에 배치된 파일은 동기화 시 업로드되며 런타임에 액세스할 수 있습니다.
- src/: 애플리케이션을 코드로 정의하는 주요 위치:
application.config.ts: 앱의 전역 구성(메타데이터 및 런타임 연결)입니다. 아래의 “Application config”를 참조하세요.*.role.ts: 로직 함수에서 사용하는 역할 정의. 아래의 “Default function role”을 참조하세요.*.object.ts: 사용자 정의 객체 정의.*.function.ts: 로직 함수 정의.*.front-component.tsx: 프런트 컴포넌트 정의.
yarn app:generate는generated/폴더를 생성합니다(타입드 Twenty 클라이언트 + 워크스페이스 타입).yarn entity:add는 사용자 정의 객체, 함수, 프런트 컴포넌트 또는 역할에 대한 엔티티 정의 파일을src/아래에 추가합니다.
인증
처음yarn auth:login을 실행하면 다음을 입력하라는 프롬프트가 표시됩니다:
- API URL(기본값은 http://localhost:3000 또는 현재 워크스페이스 프로필)
- API 키
~/.twenty/config.json에 저장됩니다. 여러 프로필을 유지하고 프로필 간에 전환할 수 있습니다.
작업 공간 관리
auth:switch로 작업 공간을 전환하면, 이후의 모든 명령은 기본적으로 해당 작업 공간을 사용합니다. 여전히 --workspace <name>로 일시적으로 재정의할 수 있습니다.
SDK 리소스(타입 및 구성) 사용
twenty-sdk는 앱 내부에서 사용하는 타입드 빌딩 블록과 헬퍼 함수를 제공합니다. 가장 자주 사용하게 될 핵심 요소는 다음과 같습니다.헬퍼 함수
SDK는 앱 엔티티를 정의할 때 사용할 수 있는, 내장 검증이 포함된 네 가지 헬퍼 함수를 제공합니다:| 함수 | 목적 |
|---|---|
defineApplication() | 애플리케이션 메타데이터 구성 |
defineObject() | 필드가 있는 사용자 정의 객체 정의 |
defineFunction() | 핸들러가 있는 로직 함수 정의 |
defineRole() | 역할 권한과 객체 접근 구성 |
객체 정의하기
사용자 정의 객체는 워크스페이스의 레코드에 대한 스키마와 동작을 모두 정의합니다.defineObject()를 사용해 내장 검증과 함께 객체를 정의하세요:
- 내장 검증과 더 나은 IDE 지원을 위해
defineObject()를 사용하세요. universalIdentifier는 배포 전반에서 고유하고 안정적이어야 합니다.- 각 필드는
name,type,label및 고유하고 안정적인universalIdentifier가 필요합니다. fields배열은 선택 사항입니다. 사용자 정의 필드 없이도 객체를 정의할 수 있습니다.yarn entity:add를 사용하여 새 객체를 스캐폴딩할 수 있으며, 이름, 필드, 관계 설정 과정을 안내합니다.
기본 필드는 자동으로 생성됩니다. 사용자 정의 객체를 정의하면 Twenty가
name, createdAt, updatedAt, createdBy, position, deletedAt 등의 표준 필드를 자동으로 추가합니다. 이 필드들은 fields 배열에 정의할 필요가 없습니다. 사용자 정의 필드만 추가하세요.애플리케이션 구성(application.config.ts)
모든 앱에는 다음을 설명하는 단일application.config.ts 파일이 있습니다:
- 앱에 대한 정보: 식별자, 표시 이름, 설명.
- 함수가 실행되는 방식: 권한을 위해 사용하는 역할.
- (선택 사항) 변수: 함수에 환경 변수로 노출되는 키–값 쌍.
defineApplication() to define your application configuration:
universalIdentifier필드는 고유하고 결정적인 ID입니다. 한 번 생성한 후 동기화 전반에 걸쳐 안정적으로 유지하세요.applicationVariables는 함수의 환경 변수가 됩니다(예:DEFAULT_RECIPIENT_NAME는process.env.DEFAULT_RECIPIENT_NAME로 사용 가능).roleUniversalIdentifiermust match the role you define in your*.role.tsfile (see below).
역할 및 권한
애플리케이션은 워크스페이스의 객체와 작업에 대한 권한을 캡슐화하는 역할을 정의할 수 있습니다. The fieldroleUniversalIdentifier in application.config.ts designates the default role used by your app’s logic functions.
TWENTY_API_KEY로 주입되는 런타임 API 키는 이 기본 함수 역할에서 파생됩니다.- 타입드 클라이언트는 해당 역할에 부여된 권한으로 제한됩니다.
- 최소 권한 원칙을 따르세요. 함수에 필요한 권한만 가진 전용 역할을 만들고, 해당 역할의 universal identifier를 참조하세요.
기본 함수 역할(*.role.ts)
새 앱을 스캐폴딩하면, CLI가 기본 역할 파일도 생성합니다.defineRole()을 사용해 내장 검증과 함께 역할을 정의하세요:
universalIdentifier of this role is then referenced in application.config.ts as roleUniversalIdentifier. 다시 말해:
- *.role.ts는 기본 함수 역할이 수행할 수 있는 작업을 정의합니다.
- application.config.ts는 해당 역할을 가리키므로, 함수는 그 권한을 상속받습니다.
- 스캐폴딩된 역할에서 시작하여, 최소 권한 원칙에 따라 점진적으로 제한하세요.
objectPermissions와fieldPermissions를 함수에 필요한 객체/필드로 교체하세요.permissionFlags는 플랫폼 수준 기능에 대한 액세스를 제어합니다. 최소한으로 유지하고, 필요한 것만 추가하세요.- Hello World 앱의 동작 예제를 참조하세요:
packages/twenty-apps/hello-world/src/roles/function-role.ts.
로직 함수 구성과 엔트리포인트
각 함수 파일은defineFunction()을 사용해 핸들러와 선택적 트리거가 포함된 구성을 내보냅니다. 자동 감지를 위해 *.function.ts 파일 접미사를 사용하세요.
- route:
/s/엔드포인트 아래에서 HTTP 경로와 메서드로 함수를 노출합니다:
예:path: '/post-card/create',-><APP_URL>/s/post-card/create에서 호출
- cron: CRON 식을 사용하여 예약된 일정으로 함수를 실행합니다.
- databaseEvent: 워크스페이스 객체 라이프사이클 이벤트에서 실행됩니다. 이벤트 작업이
updated인 경우, 수신할 특정 필드를updatedFields배열에 지정할 수 있습니다. 정의하지 않거나 비워두면, 어떤 업데이트든 함수가 트리거됩니다.
예: person.updated
노트:
triggers배열은 선택 사항입니다. 트리거가 없는 함수는 다른 함수에서 호출되는 유틸리티 함수로 사용할 수 있습니다.- 하나의 함수에서 여러 트리거 유형을 혼합할 수 있습니다.
라우트 트리거 페이로드
라우트 트리거가 로직 함수를 호출하면, AWS HTTP API v2 형식을 따르는RoutePayload 객체를 받습니다. twenty-sdk에서 해당 타입을 임포트하세요:
RoutePayload 타입은 다음과 같은 구조입니다:
| 속성 | 유형 | 설명 |
|---|---|---|
headers | Record<string, string | undefined> | HTTP 헤더(forwardedRequestHeaders에 나열된 항목만) |
queryStringParameters | Record<string, string | undefined> | 쿼리 문자열 매개변수(여러 값은 쉼표로 연결됨) |
pathParameters | Record<string, string | undefined> | 라우트 패턴에서 추출된 경로 매개변수(예: /users/:id → { id: '123' }) |
본문 | object | null | 파싱된 요청 본문(JSON) |
isBase64Encoded | 부울 | 본문이 base64로 인코딩되었는지 여부 |
requestContext.http.method | string | HTTP 메서드(GET, POST, PUT, PATCH, DELETE) |
requestContext.http.path | string | 원시 요청 경로 |
HTTP 헤더 전달
기본적으로 보안상의 이유로 들어오는 요청의 HTTP 헤더는 로직 함수로 전달되지 않습니다. 특정 헤더에 접근하려면forwardedRequestHeaders 배열에 명시적으로 나열하세요:
헤더 이름은 소문자로 정규화됩니다. 소문자 키를 사용해 접근하세요(예:
event.headers['content-type']).- 스캐폴딩:
yarn entity:add를 실행하고 새 함수를 추가하는 옵션을 선택하세요. 이렇게 하면 핸들러와 구성이 포함된 시작 파일이 생성됩니다. - 수동: 새
*.function.ts파일을 만들고 동일한 패턴에 따라defineFunction()을 사용하세요.
생성된 타입드 클라이언트
워크스페이스 스키마를 기반으로 generated/에 로컬 타입드 클라이언트를 생성하려면 yarn app:generate를 실행하세요. 함수에서 사용하세요:yarn app:generate로 다시 생성됩니다. 객체를 변경한 후 또는 새 워크스페이스에 온보딩할 때 다시 실행하세요.
로직 함수의 런타임 자격 증명
함수가 Twenty에서 실행될 때, 플랫폼은 코드가 실행되기 전에 자격 증명을 환경 변수로 주입합니다:TWENTY_API_URL: 앱이 대상으로 하는 Twenty API의 기본 URL.TWENTY_API_KEY: 애플리케이션의 기본 함수 역할 범위로 제한된 단기 키.
- 생성된 클라이언트에 URL이나 API 키를 전달할 필요가 없습니다. 런타임에 process.env에서
TWENTY_API_URL과TWENTY_API_KEY를 읽습니다. - The API key’s permissions are determined by the role referenced in your
application.config.tsviaroleUniversalIdentifier. 이는 애플리케이션의 로직 함수에서 사용하는 기본 역할입니다. - 애플리케이션은 최소 권한 원칙을 따르도록 역할을 정의할 수 있습니다. Grant only the permissions your functions need, then point
roleUniversalIdentifierto that role’s universal identifier.
Hello World 예제
객체, 함수, 여러 트리거를 보여주는 최소한의 엔드투엔드 예제를 여기에서 살펴보세요:수동 설정(스캐폴더 없이)
최적의 시작 경험을 위해create-twenty-app 사용을 권장하지만, 프로젝트를 수동으로 설정할 수도 있습니다. CLI를 전역으로 설치하지 마세요. 대신 twenty-sdk를 로컬 종속성으로 추가하고 package.json에 스크립트를 연결하세요:
yarn app:dev, yarn app:generate 등.
문제 해결
- 인증 오류:
yarn auth:login를 실행하고 API 키에 필요한 권한이 있는지 확인하세요. - 서버에 연결할 수 없음: API URL과 Twenty 서버에 접근 가능한지 확인하세요.
- 타입 또는 클라이언트가 없거나 오래된 경우:
yarn app:generate를 실행하세요. - 개발 모드가 동기화되지 않음:
yarn app:dev가 실행 중인지, 환경에서 변경 사항을 무시하지 않는지 확인하세요.