i18n - docs translations (#17434)

Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
github-actions[bot]
2026-01-26 09:06:17 +01:00
committed by GitHub
parent 2353bc62cc
commit e0d4492013
651 changed files with 37463 additions and 32557 deletions
@@ -1,147 +1,147 @@
---
title: APIs
description: Query and modify your CRM data programmatically using REST or GraphQL.
title: API
description: REST 또는 GraphQL을 사용해 프로그래밍 방식으로 CRM 데이터를 쿼리하고 수정하세요.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Twenty was built to be developer-friendly, offering powerful APIs that adapt to your custom data model. We provide four distinct API types to meet different integration needs.
Twenty는 개발자 친화적으로 설계되어 있으며, 맞춤형 데이터 모델에 적합한 강력한 API를 제공합니다. 우리는 여러 통합 요구에 맞는 네 가지 고유한 API 유형을 제공합니다.
## Developer-First Approach
## 개발자 우선 접근 방식
Twenty generates APIs specifically for your data model:
Twenty는 귀하의 데이터 모델에 맞는 API를 특별히 생성합니다:
* **No long IDs required**: Use your object and field names directly in endpoints
* **Standard and custom objects treated equally**: Your custom objects get the same API treatment as built-in ones
* **Dedicated endpoints**: Each object and field gets its own API endpoint
* **Custom documentation**: Generated specifically for your workspace's data model
* **긴 ID가 필요하지 않습니다**: 객체 및 필드 이름을 직접 엔드포인트에 사용합니다.
* **표준 및 사용자 정의 객체가 동등하게 처리됩니다**: 내장된 객체와 동일한 API 처리를 사용자 정의 객체에도 제공합니다.
* **전용 엔드포인트**: 각 객체와 필드에 자체 API 엔드포인트가 할당됩니다.
* **맞춤형 문서**: 작업 공간의 데이터 모델에 맞게 특별히 생성됩니다.
<Note>
Your personalized API documentation is available under **Settings → API & Webhooks** after creating an API key. Since Twenty generates APIs that match your custom data model, the documentation is unique to your workspace.
맞춤형 API 문서는 API 키 생성 후 **설정 → API 및 웹훅**에서 확인할 수 있습니다. Twenty가 사용자 지정 데이터 모델에 맞는 API를 생성하므로, 문서는 귀하의 워크스페이스에 고유합니다.
</Note>
## The Two API Types
## 두 가지 API 유형
### Core API
Accessed on `/rest/` or `/graphql/`
`/rest/` 또는 `/graphql/`에서 접근할 수 있습니다.
Work with your actual **records** (the data):
실제 **레코드**(데이터)로 작업합니다:
* Create, read, update, delete People, Companies, Opportunities, etc.
* Query and filter data
* Manage record relationships
* People, Companies, Opportunities 등을 생성, 조회, 업데이트, 삭제합니다.
* 데이터를 쿼리하고 필터링합니다
* 데이터 기록의 관계를 관리합니다.
### Metadata API
### 메타데이터 API
Accessed on `/rest/metadata/` or `/metadata/`
`/rest/metadata/` 또는 `/metadata/`에서 접근할 수 있습니다.
Manage your **workspace and data model**:
**워크스페이스와 데이터 모델** 관리:
* Create, modify, or delete objects and fields
* Configure workspace settings
* Define relationships between objects
* 객체 및 필드를 생성, 수정 또는 삭제합니다.
* 작업 공간 설정을 구성합니다.
* 객체 간 관계를 정의합니다
## REST vs GraphQL
## REST GraphQL
Both Core and Metadata APIs are available in REST and GraphQL formats:
코어 및 메타데이터 API는 REST GraphQL 형식으로 제공됩니다:
| Format | Available Operations |
| ----------- | ---------------------------------------------------------- |
| **REST** | CRUD, batch operations, upserts |
| **GraphQL** | Same + **batch upserts**, relationship queries in one call |
| 형식 | 사용 가능한 작업 |
| ----------- | ------------------------------- |
| **REST** | CRUD, 배치 작업, 업서트 |
| **GraphQL** | 동일 + **배치 업서트**, 한 번의 호출로 관계 쿼리 |
Choose based on your needs — both formats access the same data.
필요에 따라 선택하세요 — 두 형식 모두 동일한 데이터에 접근합니다.
## API Endpoints
## API 엔드포인트
| Environment | Base URL |
| --------------- | ------------------------- |
| **Cloud** | `https://api.twenty.com/` |
| **Self-Hosted** | `https://{your-domain}/` |
| 환경 | 기본 URL |
| ---------- | ------------------------- |
| **클라우드** | `https://api.twenty.com/` |
| **셀프 호스팅** | `https://{your-domain}/` |
## Authentication
## 인증
Every API request requires an API key in the header:
모든 API 요청에는 헤더에 API 키가 필요합니다:
```
Authorization: Bearer YOUR_API_KEY
```
### Create an API Key
### API 키 생성
1. Go to **Settings → APIs & Webhooks**
2. Click **+ Create key**
3. Configure:
* **Name**: Descriptive name for the key
* **Expiration Date**: When the key expires
4. Click **Save**
5. **Copy immediately** — the key is only shown once
1. **설정 → API 및 웹훅**으로 이동하세요
2. **+ 키 생성**을 클릭하세요
3. 구성:
* **이름**: 키를 설명하는 이름
* **만료 날짜**: 키가 만료되는 시점
4. **저장** 클릭
5. **즉시 복사** — 키는 한 번만 표시됩니다
<VimeoEmbed videoId="928786722" title="Creating API key" />
<VimeoEmbed videoId="928786722" title="API 키 생성" />
<Warning>
Your API key grants access to sensitive data. Don't share it with untrusted services. If compromised, disable it immediately and generate a new one.
API 키는 민감한 데이터에 대한 액세스를 부여합니다. 신뢰할 수 없는 서비스와 공유하지 마세요. 유출되었으면 즉시 비활성화하고 새 키를 생성하세요.
</Warning>
### Assign a Role to an API Key
### API 키에 역할 할당
For better security, assign a specific role to limit access:
보안을 강화하려면 액세스를 제한할 특정 역할을 할당하세요:
1. Go to **Settings → Roles**
2. Click on the role to assign
3. Open the **Assignment** tab
4. Under **API Keys**, click **+ Assign to API key**
5. Select the API key
1. **설정 → 역할**로 이동
2. 할당할 역할을 클릭하세요
3. **배정** 탭 열기
4. **API 키**에서 **+ API 키에 할당**을 클릭하세요
5. API 키를 선택하세요
The key will inherit that role's permissions. See [Permissions](/l/ko/user-guide/permissions-access/capabilities/permissions) for details.
키가 해당 역할의 권한을 상속받습니다. 자세한 내용은 [권한](/l/ko/user-guide/permissions-access/capabilities/permissions)을 참조하세요.
### Manage API Keys
### API 키 관리
**Regenerate**: Settings → APIs & Webhooks → Click key → **Regenerate**
**재생성**: 설정 → API 및 웹훅 → 키 클릭 → **재생성**
**Delete**: Settings → APIs & Webhooks → Click key → **Delete**
**삭제**: 설정 → API 및 웹훅 → 키 클릭 → **삭제**
## API Playground
## API 플레이그라운드
Test your APIs directly in the browser with our built-in playground — available for both **REST** and **GraphQL**.
내장 플레이그라운드를 사용해 브라우저에서 직접 API를 테스트하세요 — **REST** **GraphQL** 모두에 대해 사용할 수 있습니다.
### Access the Playground
### 플레이그라운드에 액세스
1. Go to **Settings → APIs & Webhooks**
2. Create an API key (required)
3. Click on **REST API** or **GraphQL API** to open the playground
1. **설정 → API 및 웹훅**으로 이동하세요
2. API 키 생성(필수)
3. 플레이그라운드를 열려면 **REST API** 또는 **GraphQL API**를 클릭하세요
### What You Get
### 얻을 수 있는 내용
* **Interactive documentation**: Generated for your specific data model
* **Live testing**: Execute real API calls against your workspace
* **Schema explorer**: Browse available objects, fields, and relationships
* **Request builder**: Construct queries with autocomplete
* **대화형 문서**: 귀하의 특정 데이터 모델에 맞게 생성됩니다
* 워크스페이스를 대상으로 실제 API 호출을 실행합니다
* **스키마 탐색기**: 사용 가능한 객체, 필드 및 관계를 탐색합니다
* **요청 빌더**: 자동 완성을 통해 쿼리를 구성합니다
The playground reflects your custom objects and fields, so documentation is always accurate for your workspace.
플레이그라운드는 사용자 지정 객체와 필드를 반영하므로 문서는 항상 워크스페이스에 대해 정확합니다.
## Batch Operations
## 배치 작업
Both REST and GraphQL support batch operations:
REST와 GraphQL 모두 배치 작업을 지원합니다:
* **Batch size**: Up to 60 records per request
* **Operations**: Create, update, delete multiple records
* **배치 크기**: 요청당 최대 60개의 기록
* **작업**: 여러 기록을 생성, 업데이트, 삭제합니다
**GraphQL-only features:**
**GraphQL 전용 기능:**
* **Batch Upsert**: Create or update in one call
* Use plural object names (e.g., `CreateCompanies` instead of `CreateCompany`)
* **배치 업서트**: 한 번의 호출로 생성하거나 업데이트합니다
* 복수형 객체 이름을 사용하세요(예: `CreateCompanies` 대신 `CreateCompany`)
## Rate Limits
## 속도 제한
API requests are throttled to ensure platform stability:
플랫폼 안정성을 위해 API 요청에는 제한이 적용됩니다:
| Limit | Value |
| -------------- | -------------------- |
| **Requests** | 100 calls per minute |
| **Batch size** | 60 records per call |
| 제한 | |
| --------- | ---------- |
| **요청** | 분당 100회 호출 |
| **배치 크기** | 호출당 기록 60개 |
<Tip>
Use batch operations to maximize throughput — process up to 60 records in a single API call instead of making individual requests.
처리량을 극대화하려면 배치 작업을 사용하세요 — 개별 요청 대신 단일 API 호출로 최대 60개의 기록을 처리할 수 있습니다.
</Tip>
@@ -1,81 +1,88 @@
---
title: Twenty Apps
description: Build and manage Twenty customizations as code.
title: Twenty
description: Twenty 맞춤설정을 코드로 구축하고 관리하세요.
---
<Warning>
Apps are currently in alpha testing. The feature is functional but still evolving.
앱은 현재 알파 테스트 중입니다. 해당 기능은 작동하지만 아직 발전 중입니다.
</Warning>
## What Are Apps?
## 앱이란 무엇인가요?
Apps let you build and manage Twenty customizations **as code**. Instead of configuring everything through the UI, you define your data model and serverless functions in code — making it faster to build, maintain, and roll out to multiple workspaces.
앱을 사용하면 Twenty 맞춤설정을 **코드로** 구축하고 관리할 수 있습니다. 모든 것을 UI에서 구성하는 대신, 데이터 모델과 서버리스 함수를 코드로 정의합니다 — 이를 통해 더 빠르게 구축·유지 관리하고 여러 워크스페이스에 배포할 수 있습니다.
**What you can do today:**
**현재 가능한 작업:**
* Define custom objects and fields as code (managed data model)
* Build serverless functions with custom triggers
* Deploy the same app across multiple workspaces
* 사용자 정의 객체와 필드를 코드로 정의하기(관리형 데이터 모델)
* 사용자 정의 트리거를 사용하여 서버리스 함수를 구축
* 동일한 앱을 여러 워크스페이스에 배포
**Coming soon:**
**곧 제공 예정:**
* Custom UI layouts and components
* 사용자 정의 UI 레이아웃 및 컴포넌트
## Prerequisites
## 사전 준비
* Node.js 24+ and Yarn 4
* A Twenty workspace and an API key (create one at https://app.twenty.com/settings/api-webhooks)
* Node.js 24+ Yarn 4
* Twenty 워크스페이스와 API 키(https://app.twenty.com/settings/api-webhooks에서 생성)
## Getting Started
## 시작하기
Create a new app using the official scaffolder, then authenticate and start developing:
공식 스캐폴더를 사용해 새 앱을 만든 다음, 인증하고 개발을 시작하세요:
```bash filename="Terminal"
# Scaffold a new app
# 새 앱 초기 구조 생성
npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
# Authenticate using your API key (you'll be prompted)
yarn auth
# yarn@4를 사용하지 않는 경우
corepack enable
yarn install
# Start dev mode: automatically syncs local changes to your workspace
yarn dev
# API 키로 인증합니다(입력하라는 메시지가 표시됩니다)
yarn auth:login
# 개발 모드 시작: 로컬 변경 사항이 워크스페이스와 자동으로 동기화됩니다
yarn app:dev
```
From here you can:
여기에서 다음 작업을 수행할 수 있습니다:
```bash filename="Terminal"
# Add a new entity to your application (guided)
yarn create-entity
yarn app:create-entity
# Generate a typed Twenty client and workspace entity types
yarn generate
yarn app:generate
# Run a onetime sync (instead of watch mode)
yarn sync
yarn app:sync
# Watch your application's functions logs
yarn logs
yarn function:logs
# Execute a function by name
yarn function:execute -n my-function -p '{"name": "test"}'
# Uninstall the application from the current workspace
yarn uninstall
yarn app:uninstall
# Display commands' help
yarn help
yarn app:help
```
See also: the CLI reference pages for [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) and [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk).
참고: [create-twenty-app](https://www.npmjs.com/package/create-twenty-app) [twenty-sdk CLI](https://www.npmjs.com/package/twenty-sdk)의 CLI 참고 페이지도 확인하세요.
## Project structure (scaffolded)
## 프로젝트 구조(스캐폴딩됨)
When you run `npx create-twenty-app@latest my-twenty-app`, the scaffolder:
`npx create-twenty-app@latest my-twenty-app`를 실행하면, 스캐폴더가 다음을 수행합니다:
* Copies a minimal base application into `my-twenty-app/`
* Adds a local `twenty-sdk` dependency and Yarn 4 configuration
* Creates config files and scripts wired to the `twenty` CLI
* Generates a default application config and a default function role
* `my-twenty-app/`에 최소한의 기본 애플리케이션을 복사합니다
* 로컬 `twenty-sdk` 종속성과 Yarn 4 구성을 추가합니다
* `twenty` CLI와 연결된 설정 파일과 스크립트를 생성합니다
* 기본 애플리케이션 구성과 기본 함수 역할을 생성합니다
A freshly scaffolded app looks like this:
새로 스캐폴딩된 앱은 다음과 같습니다:
```text filename="my-twenty-app/"
my-twenty-app/
@@ -85,79 +92,144 @@ my-twenty-app/
.nvmrc
.yarnrc.yml
.yarn/
releases/
yarn-4.9.2.cjs
install-state.gz
eslint.config.mjs
tsconfig.json
README.md
src/
application.config.ts
role.config.ts
// your entities, actions, and other app files
app/
application.config.ts # Required - main application configuration
default-function.role.ts # Default role for serverless functions
// your entities (*.object.ts, *.function.ts, *.role.ts)
utils/ # Optional - handler implementations & utilities
```
At a high level:
### 설정보다 관례
* **package.json**: Declares the app name, version, engines (Node 24+, Yarn 4), and adds `twenty-sdk` plus scripts like `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall`, and `auth` that delegate to the local `twenty` CLI.
* **.gitignore**: Ignores common artifacts such as `node_modules`, `.yarn`, `generated/` (typed client), `dist/`, `build/`, coverage folders, log files, and `.env*` files.
* **yarn.lock**, **.yarnrc.yml**, **.yarn/**: Lock and configure the Yarn 4 toolchain used by the project.
* **.nvmrc**: Pins the Node.js version expected by the project.
* **eslint.config.mjs** and **tsconfig.json**: Provide linting and TypeScript configuration for your apps TypeScript sources.
* **README.md**: A short README in the app root with basic instructions.
* **src/**: The main place where you define your application-as-code:
* `application.config.ts`: Global configuration for your app (metadata and runtime wiring). See “Application config” below.
* `role.config.ts`: Default function role used by your serverless functions. See “Default function role” below.
* Future entities, actions/functions, and any supporting code you add.
애플리케이션은 파일 접미사로 엔티티를 감지하는 **관례 우선** 접근 방식을 사용합니다. 이를 통해 `src/app/` 폴더 내에서 유연하게 구성할 수 있습니다:
Later commands will add more files and folders:
| 파일 접미사 | 엔티티 유형 |
| --------------- | ------------ |
| `*.object.ts` | 사용자 정의 객체 정의 |
| `*.function.ts` | 서버리스 함수 정의 |
| `*.role.ts` | 역할 정의 |
* `yarn generate` will create a `generated/` folder (typed Twenty client + workspace types).
* `yarn create-entity` will add entity definition files under `src/` for your custom objects.
### 지원되는 폴더 구성 방식
## Authentication
엔티티를 다음 패턴 중 어느 것으로든 구성할 수 있습니다:
The first time you run `yarn auth`, you'll be prompted for:
**전통적(유형별):**
* API URL (defaults to http://localhost:3000 or your current workspace profile)
* API key
```text
src/app/
├── application.config.ts
├── objects/
│ └── postCard.object.ts
├── functions/
│ └── createPostCard.function.ts
└── roles/
└── admin.role.ts
```
Your credentials are stored per-user in `~/.twenty/config.json`. You can maintain multiple profiles and switch using `--workspace <name>`.
**기능 기반:**
Examples:
```text
src/app/
├── application.config.ts
└── post-card/
├── postCard.object.ts
├── createPostCard.function.ts
└── postCardAdmin.role.ts
```
**플랫:**
```text
src/app/
├── application.config.ts
├── postCard.object.ts
├── createPostCard.function.ts
└── admin.role.ts
```
개요:
* **package.json**: 앱 이름, 버전, 엔진(Node 24+, Yarn 4)을 선언하고, `twenty-sdk`와 함께 `dev`, `sync`, `generate`, `create-entity`, `logs`, `uninstall`, `auth` 같은 스크립트를 추가합니다. 이 스크립트들은 로컬 `twenty` CLI에 위임됩니다.
* **.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입니다.
* **src/app/**: 애플리케이션을 코드로 정의하는 주요 위치:
* `application.config.ts`: 앱의 전역 구성(메타데이터 및 런타임 연결)입니다. 아래의 "Application config"를 참조하세요.
* `*.role.ts`: 서버리스 함수에서 사용하는 역할 정의. 아래의 "Default function role"을 참조하세요.
* `*.object.ts`: 사용자 정의 객체 정의.
* `*.function.ts`: 서버리스 함수 정의.
* **src/utils/**: 핸들러 구현 및 유틸리티를 위한 선택적 폴더.
이후 명령을 실행하면 더 많은 파일과 폴더가 추가됩니다:
* `yarn app:generate`는 `generated/` 폴더를 생성합니다(타입드 Twenty 클라이언트 + 워크스페이스 타입).
* `yarn app:create-entity`는 사용자 정의 객체, 함수, 역할에 대한 엔티티 정의 파일을 `src/app/` 아래에 추가합니다.
l
## 인증
처음 `yarn auth:login`을 실행하면 다음을 입력하라는 프롬프트가 표시됩니다:
* API URL(기본값은 http://localhost:3000 또는 현재 워크스페이스 프로필)
* API 키
자격 증명은 사용자별로 `~/.twenty/config.json`에 저장됩니다. 여러 프로필을 유지하고 프로필 간에 전환할 수 있습니다.
### 작업 공간 관리
```bash filename="Terminal"
# Login interactively (recommended)
yarn auth
yarn auth:login
# Use a specific workspace profile
yarn auth --workspace my-custom-workspace
# Login to a specific workspace profile
yarn auth:login --workspace my-custom-workspace
# List all configured workspaces
yarn auth:list
# Switch the default workspace (interactive)
yarn auth:switch
# Switch to a specific workspace
yarn auth:switch production
# Check current authentication status
yarn auth:status
```
## Use the SDK resources (types & config)
한 번 `auth:switch`로 작업 공간을 전환하면, 이후의 모든 명령은 기본적으로 해당 작업 공간을 사용합니다. 여전히 `--workspace <name>`로 일시적으로 재정의할 수 있습니다.
The twenty-sdk provides typed building blocks you use inside your app. Below are the key pieces you'll touch most often.
## SDK 리소스(타입 및 구성) 사용
### Defining objects
twenty-sdk는 앱 내부에서 사용하는 타입드 빌딩 블록과 헬퍼 함수를 제공합니다. 가장 자주 사용하게 될 핵심 요소는 다음과 같습니다.
Custom objects are regular TypeScript classes annotated with decorators from `twenty-sdk`. They live under `src/objects/` in your app and describe both schema and behavior for records in your workspace.
### 헬퍼 함수
Here is an example `postCard` object from the Hello World app:
SDK는 앱 엔티티를 정의할 때 사용할 수 있는, 내장 검증이 포함된 네 가지 헬퍼 함수를 제공합니다:
| 함수 | 목적 |
| ------------------ | ------------------- |
| `defineApp()` | 애플리케이션 메타데이터 구성 |
| `defineObject()` | 필드가 있는 사용자 정의 객체 정의 |
| `defineFunction()` | 핸들러가 있는 서버리스 함수 정의 |
| `defineRole()` | 역할 권한과 객체 접근 구성 |
이 함수들은 런타임에 구성을 검증하고, 더 나은 IDE 자동 완성과 타입 안정성을 제공합니다.
### 객체 정의하기
사용자 정의 객체는 워크스페이스의 레코드에 대한 스키마와 동작을 모두 정의합니다. `defineObject()`를 사용해 내장 검증과 함께 객체를 정의하세요:
```typescript
import { type Note } from '../../generated';
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
// src/app/postCard.object.ts
import { defineObject, FieldType } from 'twenty-sdk';
enum PostCardStatus {
DRAFT = 'DRAFT',
@@ -166,176 +238,186 @@ enum PostCardStatus {
RETURNED = 'RETURNED',
}
@Object({
export default defineObject({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: ' A post card object',
labelSingular: 'Post Card',
labelPlural: 'Post Cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Field({
universalIdentifier: 'c6aa31f3-da76-4ac6-889f-475e226009ac',
type: FieldType.FULL_NAME,
label: 'Recipient name',
icon: 'IconUser',
})
recipientName: FullNameField;
@Field({
universalIdentifier: '95045777-a0ad-49ec-98f9-22f9fc0c8266',
type: FieldType.ADDRESS,
label: 'Recipient address',
icon: 'IconHome',
})
recipientAddress: AddressField;
@Field({
universalIdentifier: '87b675b8-dd8c-4448-b4ca-20e5a2234a1e',
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' },
],
})
status: PostCardStatus;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
@Field({
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
type: FieldType.DATE_TIME,
label: 'Delivered at',
icon: 'IconCheck',
isNullable: true,
defaultValue: null,
})
deliveredAt?: Date;
}
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,
},
],
});
```
Key points:
핵심 요점:
* The `@Object` decorator defines the object identity and labels used across the workspace; its `universalIdentifier` must be unique and stable across deployments.
* Each `@Field` decorator defines a field on the object with a type, label, and its own stable `universalIdentifier`.
* `@Relation` wires this object to other objects (standard or custom) and controls cascade behavior with `onDelete`.
* You can scaffold new objects using `yarn create-entity`, which guides you through naming, fields, and relationships, then generates object files similar to the `postCard` example.
* 내장 검증과 더 나은 IDE 지원을 위해 `defineObject()`를 사용하세요.
* `universalIdentifier`는 배포 전반에서 고유하고 안정적이어야 합니다.
* 각 필드는 `name`, `type`, `label` 및 고유하고 안정적인 `universalIdentifier`가 필요합니다.
* `fields` 배열은 선택 사항입니다. 사용자 정의 필드 없이도 객체를 정의할 수 있습니다.
* `yarn app:create-entity`를 사용하여 새 객체를 스캐폴딩할 수 있으며, 이름, 필드, 관계 설정 과정을 안내합니다.
### Application config (application.config.ts)
<Note>
**기본 필드는 자동으로 생성됩니다.** 사용자 정의 객체를 정의하면 Twenty가 `name`, `createdAt`, `updatedAt`, `createdBy`, `position`, `deletedAt` 등의 표준 필드를 자동으로 추가합니다. 이 필드들은 `fields` 배열에 정의할 필요가 없습니다. 사용자 정의 필드만 추가하세요.
</Note>
Every app has a single `application.config.ts` file that describes:
<Accordion title="대안: 데코레이터 기반 문법">
TypeScript 데코레이터를 사용하여 객체를 정의할 수도 있습니다. 이 접근 방식은 `@Object`, `@Field`, `@Relation` 데코레이터를 사용하는 클래스 기반 문법을 사용합니다:
* **Who the app is**: identifiers, display name, and description.
* **How its functions run**: which role they use for permissions.
* **(Optional) variables**: keyvalue pairs exposed to your functions as environment variables.
```typescript
import {
type AddressField,
Field,
FieldType,
type FullNameField,
Object,
OnDeleteAction,
Relation,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk';
import { type Note } from '../../generated';
When you scaffold a new app, you start with a minimal config:
@Object({
universalIdentifier: '54b589ca-eeed-4950-a176-358418b85c05',
nameSingular: 'postCard',
namePlural: 'postCards',
labelSingular: 'Post card',
labelPlural: 'Post cards',
description: 'A post card object',
icon: 'IconMail',
})
export class PostCard {
@Field({
universalIdentifier: '58a0a314-d7ea-4865-9850-7fb84e72f30b',
type: FieldType.TEXT,
label: 'Content',
description: "Postcard's content",
icon: 'IconAbc',
})
content: string;
@Relation({
universalIdentifier: 'c9e2b4f4-b9ad-4427-9b42-9971b785edfe',
type: RelationType.ONE_TO_MANY,
label: 'Notes',
icon: 'IconComment',
inverseSideTargetUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note,
onDelete: OnDeleteAction.CASCADE,
})
notes: Note[];
}
```
참고: 데코레이터 접근 방식은 TypeScript 구성에서 `experimentalDecorators`가 필요합니다.
</Accordion>
### 애플리케이션 구성(application.config.ts)
모든 앱에는 다음을 설명하는 단일 `application.config.ts` 파일이 있습니다:
* **앱에 대한 정보**: 식별자, 표시 이름, 설명.
* **함수가 실행되는 방식**: 권한을 위해 사용하는 역할.
* **(선택 사항) 변수**: 함수에 환경 변수로 노출되는 키–값 쌍.
`defineApp()`을 사용해 애플리케이션 구성을 정의하세요:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
// src/app/application.config.ts
import { defineApp } from 'twenty-sdk';
import { DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER } from './default-function.role';
const config: ApplicationConfig = {
universalIdentifier: '<generated-app-uuid>',
export default defineApp({
universalIdentifier: '4ec0391d-18d5-411c-b2f3-266ddc1c3ef7',
displayName: 'My Twenty App',
description: 'My first Twenty app',
functionRoleUniversalIdentifier: '<generated-role-uuid>',
};
export default config;
```
You can gradually extend this file as your app grows. For example, you can add an icon and application-scoped variables:
```typescript
import { type ApplicationConfig } from 'twenty-sdk';
const config: ApplicationConfig = {
universalIdentifier: '<your-app-uuid>',
displayName: 'My App',
description: 'What your app does',
icon: 'IconWorld', // Choose an icon by name
icon: 'IconWorld',
applicationVariables: {
DEFAULT_RECIPIENT_NAME: {
universalIdentifier: '<uuid>',
description: 'Default recipient used by functions',
universalIdentifier: '19e94e59-d4fe-4251-8981-b96d0a9f74de',
description: 'Default recipient name for postcards',
value: 'Jane Doe',
isSecret: false,
},
},
functionRoleUniversalIdentifier: '<your-role-uuid>',
};
export default config;
functionRoleUniversalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
});
```
Notes:
노트:
* `universalIdentifier` fields are deterministic IDs you own; generate them once and keep them stable across syncs.
* `applicationVariables` become environment variables for your functions (for example, `DEFAULT_RECIPIENT_NAME` is available as `process.env.DEFAULT_RECIPIENT_NAME`).
* `functionRoleUniversalIdentifier` must match the role you define in `role.config.ts` (see below).
* `universalIdentifier` 필드는 고유하고 결정적인 ID입니다. 한 번 생성한 후 동기화 전반에 걸쳐 안정적으로 유지하세요.
* `applicationVariables`는 함수의 환경 변수가 됩니다(예: `DEFAULT_RECIPIENT_NAME` `process.env.DEFAULT_RECIPIENT_NAME`로 사용 가능).
* `functionRoleUniversalIdentifier`는 `*.role.ts` 파일에서 정의한 역할과 일치해야 합니다(아래 참조).
#### Roles and permissions
#### 역할 및 권한
Applications can define roles that encapsulate permissions on your workspaces objects and actions. The field `functionRoleUniversalIdentifier` in `application.config.ts` designates the default role used by your apps serverless functions.
애플리케이션은 워크스페이스의 객체와 작업에 대한 권한을 캡슐화하는 역할을 정의할 수 있습니다. `application.config.ts`의 `functionRoleUniversalIdentifier` 필드는 앱의 서버리스 함수가 사용하는 기본 역할을 지정합니다.
* The runtime API key injected as `TWENTY_API_KEY` is derived from this default function role.
* The typed client will be restricted to the permissions granted to that role.
* Follow leastprivilege: create a dedicated role with only the permissions your functions need, then reference its universal identifier.
* `TWENTY_API_KEY`로 주입되는 런타임 API 키는 이 기본 함수 역할에서 파생됩니다.
* 타입드 클라이언트는 해당 역할에 부여된 권한으로 제한됩니다.
* 최소 권한 원칙을 따르세요. 함수에 필요한 권한만 가진 전용 역할을 만들고, 해당 역할의 universal identifier를 참조하세요.
##### Default function role (role.config.ts)
##### 기본 함수 역할(\*.role.ts)
When you scaffold a new app, the CLI also creates `src/role.config.ts`. This file exports the default role your serverless functions will use at runtime:
새 앱을 스캐폴딩하면, CLI가 기본 역할 파일도 생성합니다. `defineRole()`을 사용해 내장 검증과 함께 역할을 정의하세요:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
// src/app/default-function.role.ts
import { defineRole, PermissionFlag } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<generated-role-uuid>',
label: 'My Twenty App default function role',
description: 'My Twenty App default function role',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
};
```
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'b648f87b-1d26-4961-b974-0908fd991061';
The `universalIdentifier` of this role is automatically wired into `application.config.ts` as `functionRoleUniversalIdentifier`. In other words:
* **role.config.ts** defines what the default function role can do.
* **application.config.ts** points to that role so your functions inherit its permissions.
As you move beyond the initial scaffold, you should tighten this role and make it explicit about what it can access. A more production-ready role might look closer to:
```typescript
import { PermissionFlag, type RoleConfig } from 'twenty-sdk';
export const functionRole: RoleConfig = {
universalIdentifier: '<your-role-uuid>',
export default defineRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Default function role',
description: 'Default role for function Twenty client',
canReadAllObjectRecords: false,
@@ -363,41 +445,41 @@ export const functionRole: RoleConfig = {
canUpdateFieldValue: false,
},
],
permissionFlags: ['APPLICATIONS'],
};
permissionFlags: [PermissionFlag.APPLICATIONS],
});
```
Notes:
이 역할의 `universalIdentifier`는 `application.config.ts`에서 `functionRoleUniversalIdentifier`로 참조됩니다. 다시 말해:
* Start from the scaffolded role, then progressively restrict it following leastprivilege.
* Replace the `objectPermissions` and `fieldPermissions` with the objects/fields your functions need.
* `permissionFlags` control access to platform-level capabilities. Keep them minimal; add only what you need.
* See a working example in the Hello World app: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
* **\*.role.ts**는 기본 함수 역할이 수행할 수 있는 작업을 정의합니다.
* **application.config.ts**는 해당 역할을 가리키므로, 함수는 그 권한을 상속받습니다.
### Serverless function config and entrypoint
노트:
Each function exports a main handler and a config describing its triggers. You can mix multiple trigger types.
* 스캐폴딩된 역할에서 시작하여, 최소 권한 원칙에 따라 점진적으로 제한하세요.
* `objectPermissions`와 `fieldPermissions`를 함수에 필요한 객체/필드로 교체하세요.
* `permissionFlags`는 플랫폼 수준 기능에 대한 액세스를 제어합니다. 최소한으로 유지하고, 필요한 것만 추가하세요.
* Hello World 앱의 동작 예제를 참조하세요: [`packages/twenty-apps/hello-world/src/roles/function-role.ts`](https://github.com/twentyhq/twenty/blob/main/packages/twenty-apps/hello-world/src/roles/function-role.ts).
### 서버리스 함수 구성과 엔트리포인트
각 함수 파일은 `defineFunction()`을 사용해 핸들러와 선택적 트리거가 포함된 구성을 내보냅니다. 자동 감지를 위해 `*.function.ts` 파일 접미사를 사용하세요.
```typescript
// src/actions/create-new-post-card.ts
import type {
FunctionConfig,
DatabaseEventPayload,
ObjectRecordCreateEvent,
CronPayload,
} from 'twenty-sdk';
import Twenty, { type Person } from '../generated';
// src/app/createPostCard.function.ts
import { defineFunction } from 'twenty-sdk';
import type { DatabaseEventPayload, ObjectRecordCreateEvent, CronPayload, RoutePayload } from 'twenty-sdk';
import Twenty, { type Person } from '../../generated';
// main handler can accept parameters from route, cron, or database events
export const main = async (
const handler = async (
params:
| { name?: string }
| RoutePayload
| DatabaseEventPayload<ObjectRecordCreateEvent<Person>>
| CronPayload,
) => {
const client = new Twenty(); // generated typed client
const name = 'name' in params
? params.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
const name = 'name' in params.queryStringParameters
? params.queryStringParameters.name ?? process.env.DEFAULT_RECIPIENT_NAME ?? 'Hello world'
: 'Hello world';
const result = await client.mutation({
@@ -410,14 +492,15 @@ export const main = async (
return result;
};
export const config: FunctionConfig = {
universalIdentifier: '<function-uuid>',
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'create-new-post-card',
timeoutSeconds: 2,
handler,
triggers: [
// Public HTTP route trigger '/s/post-card/create'
{
universalIdentifier: '<route-trigger-uuid>',
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/post-card/create',
httpMethod: 'GET',
@@ -425,39 +508,137 @@ export const config: FunctionConfig = {
},
// Cron trigger (CRON pattern)
{
universalIdentifier: '<cron-trigger-uuid>',
universalIdentifier: 'dd802808-0695-49e1-98c9-d5c9e2704ce2',
type: 'cron',
pattern: '0 0 1 1 *',
},
// Database event trigger
{
universalIdentifier: '<db-trigger-uuid>',
universalIdentifier: '203f1df3-4a82-4d06-a001-b8cf22a31156',
type: 'databaseEvent',
eventName: 'person.created',
eventName: 'person.updated',
updatedFields: ['name'],
},
],
});
```
일반적인 트리거 유형:
* **route**: **`/s/` 엔드포인트** 아래에서 HTTP 경로와 메서드로 함수를 노출합니다:
> 예: `path: '/post-card/create',` -> `<APP_URL>/s/post-card/create`에서 호출
* **cron**: CRON 식을 사용하여 예약된 일정으로 함수를 실행합니다.
* **databaseEvent**: 워크스페이스 객체 라이프사이클 이벤트에서 실행됩니다. 이벤트 작업이 `updated`인 경우, 수신할 특정 필드를 `updatedFields` 배열에 지정할 수 있습니다. 정의하지 않거나 비워두면, 어떤 업데이트든 함수가 트리거됩니다.
> 예: `person.updated`
노트:
* `triggers` 배열은 선택 사항입니다. 트리거가 없는 함수는 다른 함수에서 호출되는 유틸리티 함수로 사용할 수 있습니다.
* 하나의 함수에서 여러 트리거 유형을 혼합할 수 있습니다.
### 라우트 트리거 페이로드
<Warning>
**호환성 파괴적 변경(v1.16, 2026년 1월):** 라우트 트리거 페이로드 형식이 변경되었습니다. v1.16 이전에는 쿼리 매개변수, 경로 매개변수, 그리고 본문이 페이로드로 직접 전송되었습니다. v1.16부터는 이들이 구조화된 `RoutePayload` 객체 내부에 중첩됩니다.
**v1.16 이전:**
```typescript
const handler = async (params) => {
const { param1, param2 } = params; // Direct access
};
```
**v1.16 이후:**
```typescript
const handler = async (event: RoutePayload) => {
const { param1, param2 } = event.body; // Access via .body
const { queryParam } = event.queryStringParameters;
const { id } = event.pathParameters;
};
```
**기존 함수 마이그레이션 방법:** 핸들러에서 params 객체에서 직접 구조 분해하는 대신 `event.body`, `event.queryStringParameters`, 또는 `event.pathParameters`에서 구조 분해하도록 업데이트하세요.
</Warning>
라우트 트리거가 함수를 호출하면, AWS HTTP API v2 형식을 따르는 `RoutePayload` 객체를 받습니다. `twenty-sdk`에서 해당 타입을 임포트하세요:
```typescript
import { defineFunction, type RoutePayload } from 'twenty-sdk';
const handler = async (event: RoutePayload) => {
// Access request data
const { headers, queryStringParameters, pathParameters, body } = event;
// HTTP method and path are available in requestContext
const { method, path } = event.requestContext.http;
return { message: 'Success' };
};
```
Common trigger types:
`RoutePayload` 타입은 다음과 같은 구조입니다:
* route: Exposes your function on an HTTP path and method **under the `/s/` endpoint**:
| 속성 | 유형 | 설명 |
| ---------------------------- | ------------------------------------- | ------------------------------------------------------- |
| `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` | 원시 요청 경로 |
> e.g. `path: '/post-card/create',` -> call on `<APP_URL>/s/post-card/create`
### HTTP 헤더 전달
* cron: Runs your function on a schedule using a CRON expression.
* databaseEvent: Runs on workspace object lifecycle events
기본적으로 보안상의 이유로 들어오는 요청의 HTTP 헤더는 서버리스 함수로 **전달되지 않습니다**. 특정 헤더에 접근하려면 `forwardedRequestHeaders` 배열에 명시적으로 나열하세요:
> e.g. `person.created`
```typescript
export default defineFunction({
universalIdentifier: 'e56d363b-0bdc-4d8a-a393-6f0d1c75bdcf',
name: 'webhook-handler',
handler,
triggers: [
{
universalIdentifier: 'c9f84c8d-b26d-40d1-95dd-4f834ae5a2c6',
type: 'route',
path: '/webhook',
httpMethod: 'POST',
isAuthRequired: false,
forwardedRequestHeaders: ['x-webhook-signature', 'content-type'],
},
],
});
```
You can create new functions in two ways:
핸들러에서 다음과 같이 해당 헤더에 접근할 수 있습니다:
* **Scaffolded**: Run `yarn create-entity --path <custom-path>` and choose the option to add a new function. This generates a starter file under `<custom-path>` with a `main` handler and a `config` block similar to the example above.
* **Manual**: Create a new file and export `main` and `config` yourself, following the same pattern.
```typescript
const handler = async (event: RoutePayload) => {
const signature = event.headers['x-webhook-signature'];
const contentType = event.headers['content-type'];
### Generated typed client
// Validate webhook signature...
return { received: true };
};
```
Run yarn generate to create a local typed client in generated/ based on your workspace schema. Use it in your functions:
<Note>
헤더 이름은 소문자로 정규화됩니다. 소문자 키를 사용해 접근하세요(예: `event.headers['content-type']`).
</Note>
새 함수를 만드는 방법은 두 가지입니다:
* **스캐폴딩**: `yarn app:create-entity`를 실행하고 새 함수를 추가하는 옵션을 선택하세요. 이렇게 하면 핸들러와 구성이 포함된 시작 파일이 생성됩니다.
* **수동**: 새 `*.function.ts` 파일을 만들고 동일한 패턴에 따라 `defineFunction()`을 사용하세요.
### 생성된 타입드 클라이언트
워크스페이스 스키마를 기반으로 generated/에 로컬 타입드 클라이언트를 생성하려면 yarn app:generate를 실행하세요. 함수에서 사용하세요:
```typescript
import Twenty from './generated';
@@ -466,34 +647,34 @@ const client = new Twenty();
const { me } = await client.query({ me: { id: true, displayName: true } });
```
The client is re-generated by `yarn generate`. Re-run after changing your objects and `yarn sync` or when onboarding to a new workspace.
클라이언트는 `yarn app:generate`로 다시 생성됩니다. 객체를 변경하고 `yarn app:sync`를 실행한 후 또는 새 워크스페이스에 온보딩할 때 다시 실행하세요.
#### Runtime credentials in serverless functions
#### 서버리스 함수의 런타임 자격 증명
When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
함수가 Twenty에서 실행될 때, 플랫폼은 코드가 실행되기 전에 자격 증명을 환경 변수로 주입합니다:
* `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
* `TWENTY_API_KEY`: Shortlived key scoped to your applications default function role.
* `TWENTY_API_URL`: 앱이 대상으로 하는 Twenty API의 기본 URL.
* `TWENTY_API_KEY`: 애플리케이션의 기본 함수 역할 범위로 제한된 단기 키.
Notes:
노트:
* You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
* The API keys permissions are determined by the role referenced in your `application.config.ts` via `functionRoleUniversalIdentifier`. This is the default role used by serverless functions of your application.
* Applications can define roles to follow leastprivilege. Grant only the permissions your functions need, then point `functionRoleUniversalIdentifier` to that roles universal identifier.
* 생성된 클라이언트에 URL이나 API 키를 전달할 필요가 없습니다. 런타임에 process.env에서 `TWENTY_API_URL` `TWENTY_API_KEY`를 읽습니다.
* API 키의 권한은 `application.config.ts`에서 `functionRoleUniversalIdentifier`를 통해 참조된 역할에 의해 결정됩니다. 이는 애플리케이션의 서버리스 함수에서 사용하는 기본 역할입니다.
* 애플리케이션은 최소 권한 원칙을 따르도록 역할을 정의할 수 있습니다. 함수에 필요한 권한만 부여하고, `functionRoleUniversalIdentifier`를 해당 역할의 universal identifier로 지정하세요.
### Hello World example
### Hello World 예제
Explore a minimal, end-to-end example that demonstrates objects, functions, and multiple triggers [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world):
객체, 함수, 여러 트리거를 보여주는 최소한의 엔드투엔드 예제를 [여기](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world)에서 살펴보세요:
## Manual setup (without the scaffolder)
## 수동 설정(스캐폴더 없이)
While we recommend using `create-twenty-app` for the best getting-started experience, you can also set up a project manually. Do not install the CLI globally. Instead, add `twenty-sdk` as a local dependency and wire scripts in your package.json:
최적의 시작 경험을 위해 `create-twenty-app` 사용을 권장하지만, 프로젝트를 수동으로 설정할 수도 있습니다. CLI를 전역으로 설치하지 마세요. 대신 `twenty-sdk`를 로컬 종속성으로 추가하고 package.json에 스크립트를 연결하세요:
```bash filename="Terminal"
yarn add -D twenty-sdk
```
Then add scripts like these:
그런 다음 다음과 같은 스크립트를 추가하세요:
```json filename="package.json"
{
@@ -510,13 +691,13 @@ Then add scripts like these:
}
```
Now you can run the same commands via Yarn, e.g. `yarn dev`, `yarn sync`, etc.
이제 Yarn을 통해 동일한 명령을 실행할 수 있습니다. 예: `yarn app:dev`, `yarn app:sync`
## Troubleshooting
## 문제 해결
* Authentication errors: run `yarn auth` and ensure your API key has the required permissions.
* Cannot connect to server: verify the API URL and that the Twenty server is reachable.
* Types or client missing/outdated: run `yarn generate` and then `yarn dev`.
* Dev mode not syncing: ensure `yarn dev` is running and that changes are not ignored by your environment.
* 인증 오류: `yarn auth:login`를 실행하고 API 키에 필요한 권한이 있는지 확인하세요.
* 서버에 연결할 수 없음: API URL과 Twenty 서버에 접근 가능한지 확인하세요.
* 타입 또는 클라이언트가 없거나 오래됨: `yarn app:generate`를 실행한 다음 `yarn app:dev`를 실행하세요.
* 개발 모드가 동기화되지 않음: `yarn app:dev`가 실행 중인지, 환경에서 변경 사항을 무시하지 않는지 확인하세요.
Discord Help Channel: https://discord.com/channels/1130383047699738754/1130386664812982322
Discord 도움말 채널: https://discord.com/channels/1130383047699738754/1130386664812982322
@@ -1,44 +1,44 @@
---
title: Webhooks
description: Receive real-time notifications when events occur in your CRM.
description: CRM에서 이벤트가 발생하면 실시간 알림을 받으세요.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
Webhooks push data to your systems in real-time when events occur in Twenty — no polling required. Use them to keep external systems in sync, trigger automations, or send alerts.
웹훅은 Twenty에서 이벤트가 발생하면 데이터를 실시간으로 귀하의 시스템으로 푸시합니다 — 폴링이 필요 없습니다. 이를 사용하여 외부 시스템을 동기화 상태로 유지하고, 자동화를 트리거하거나, 알림을 보낼 수 있습니다.
## Create a Webhook
## Webhook 생성
1. Go to **Settings → APIs & Webhooks → Webhooks**
2. Click **+ Create webhook**
3. Enter your webhook URL (must be publicly accessible)
4. Click **Save**
1. **설정 → API 및 웹훅 → 웹훅**으로 이동
2. **+ Webhook 생성** 클릭
3. 웹훅 URL을 입력하세요(공개적으로 액세스 가능해야 함)
4. **저장** 클릭
The webhook activates immediately and starts sending notifications.
웹훅이 즉시 활성화되어 알림 전송을 시작합니다.
<VimeoEmbed videoId="928786708" title="Creating a webhook" />
<VimeoEmbed videoId="928786708" title="웹훅 생성" />
### Manage Webhooks
### 웹훅 관리
**Edit**: Click the webhook → Update URL → **Save**
**편집**: 웹훅을 클릭 → URL 업데이트 → **저장**
**Delete**: Click the webhook → **Delete** → Confirm
**삭제**: 웹훅을 클릭 → **삭제** → 확인
## Events
## 이벤트
Twenty sends webhooks for these event types:
Twenty는 다음 이벤트 유형에 대해 웹훅을 전송합니다:
| Event | Example |
| ------------------ | ---------------------------------------------------------- |
| **Record Created** | `person.created`, `company.created`, `note.created` |
| **Record Updated** | `person.updated`, `company.updated`, `opportunity.updated` |
| **Record Deleted** | `person.deleted`, `company.deleted` |
| 이벤트 | 예시 |
| ------------- | ---------------------------------------------------------- |
| **레코드 생성됨** | `person.created`, `company.created`, `note.created` |
| **레코드 업데이트됨** | `person.updated`, `company.updated`, `opportunity.updated` |
| **레코드 삭제됨** | `person.deleted`, `company.deleted` |
All event types are sent to your webhook URL. Event filtering may be added in future releases.
모든 이벤트 유형은 귀하의 웹훅 URL로 전송됩니다. 이벤트 필터링은 향후 릴리스에서 추가될 수 있습니다.
## Payload Format
## 페이로드 형식
Each webhook sends an HTTP POST with a JSON body:
각 웹훅은 JSON 본문을 포함한 HTTP POST를 전송합니다:
```json
{
@@ -55,35 +55,35 @@ Each webhook sends an HTTP POST with a JSON body:
}
```
| Field | Description |
| ----------- | ------------------------------------------------ |
| `event` | What happened (e.g., `person.created`) |
| `data` | The full record that was created/updated/deleted |
| `timestamp` | When the event occurred (UTC) |
| 필드 | 설명 |
| ------- | -------------------------------- |
| `이벤트` | 무슨 일이 발생했는지(예: `person.created`) |
| `데이터` | 생성/업데이트/삭제된 전체 레코드 |
| `타임스탬프` | 이벤트가 발생한 시각(UTC) |
<Note>
Respond with a **2xx HTTP status** (200-299) to acknowledge receipt. Non-2xx responses are logged as delivery failures.
수신을 확인하기 위해 **2xx HTTP 상태**(200-299)로 응답하세요. 2xx가 아닌 응답은 전달 실패로 기록됩니다.
</Note>
## Webhook Validation
## 웹훅 검증
Twenty signs each webhook request for security. Validate signatures to ensure requests are authentic.
Twenty는 보안을 위해 각 웹훅 요청에 서명합니다. 요청의 진위를 보장하기 위해 서명을 검증하세요.
### Headers
### 헤더
| Header | Description |
| ---------------------------- | --------------------- |
| `X-Twenty-Webhook-Signature` | HMAC SHA256 signature |
| `X-Twenty-Webhook-Timestamp` | Request timestamp |
| 헤더 | 설명 |
| ---------------------------- | -------------- |
| `X-Twenty-Webhook-Signature` | HMAC SHA256 서명 |
| `X-Twenty-Webhook-Timestamp` | 요청 타임스탬프 |
### Validation Steps
### 검증 단계
1. Get the timestamp from `X-Twenty-Webhook-Timestamp`
2. Create the string: `{timestamp}:{JSON payload}`
3. Compute HMAC SHA256 using your webhook secret
4. Compare with `X-Twenty-Webhook-Signature`
1. `X-Twenty-Webhook-Timestamp`에서 타임스탬프를 가져옵니다
2. 다음 문자열을 생성합니다: `{timestamp}:{JSON payload}`
3. 웹훅 비밀을 사용하여 HMAC SHA256을 계산합니다
4. `X-Twenty-Webhook-Signature`와 비교합니다
### Example (Node.js)
### 예시(Node.js)
```javascript
const crypto = require("crypto");
@@ -101,12 +101,12 @@ const expectedSignature = crypto
const isValid = expectedSignature === req.headers["x-twenty-webhook-signature"];
```
## Webhooks vs Workflows
## 웹훅 vs 워크플로
| Method | Direction | Use Case |
| ---------------------------- | --------- | ---------------------------------------------------------- |
| **Webhooks** | OUT | Automatically notify external systems of any record change |
| **Workflow + HTTP Request** | OUT | Send data out with custom logic (filters, transformations) |
| **Workflow Webhook Trigger** | IN | Receive data into Twenty from external systems |
| 방법 | 방향 | 사용 사례 |
| ------------------ | --- | ---------------------------------- |
| **웹훅** | OUT | 레코드 변경 사항을 외부 시스템에 자동으로 알립니다 |
| **워크플로 + HTTP 요청** | OUT | 사용자 지정 로직(필터, 변환)으로 데이터를 외부로 전송합니다 |
| **워크플로 웹훅 트리거** | IN | 외부 시스템에서 Twenty로 데이터를 수신합니다 |
For receiving external data, see [Set Up a Webhook Trigger](/l/ko/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger).
외부 데이터를 수신하려면 [웹훅 트리거 설정](/l/ko/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger)을 참조하세요.
@@ -1,34 +1,34 @@
---
title: Extend
description: Extend Twenty's functionality with APIs, webhooks, and custom apps.
title: 확장
description: API, 웹훅 및 맞춤형 앱으로 Twenty의 기능을 확장하세요.
---
<Frame>
<img src="/images/user-guide/integrations/plug.png" alt="AI" />
</Frame>
## Overview
## 개요
Twenty is designed to be extensible. Use our APIs, webhooks, and app framework to integrate with your existing tools and build custom functionality.
Twenty는 확장 가능하도록 설계되었습니다. 당사의 API, 웹훅 및 앱 프레임워크를 사용하여 기존 도구와 통합하고 맞춤형 기능을 구축하세요.
## What You Can Do
## 가능한 작업
* **APIs**: Query and modify your CRM data programmatically using REST or GraphQL
* **Webhooks**: Receive real-time notifications when events occur in Twenty
* **Apps**: Build custom applications that extend Twenty's capabilities - Coming soon!
* **API**: REST 또는 GraphQL을 사용해 프로그래밍 방식으로 CRM 데이터를 쿼리하고 수정하세요
* **웹훅**: Twenty에서 이벤트가 발생하면 실시간 알림을 받으세요
* **앱**: Twenty의 기능을 확장하는 맞춤형 앱을 구축하세요 - 곧 제공됩니다!
## Getting Started
## 시작하기
<CardGroup cols={2}>
<Card title="APIs" icon="code" href="/l/ko/developers/extend/capabilities/apis">
Connect to Twenty programmatically
<Card title="API" icon="코드" href="/l/ko/developers/extend/capabilities/apis">
프로그래밍 방식으로 Twenty에 연결하세요
</Card>
<Card title="Webhooks" icon="bell" href="/l/ko/developers/extend/capabilities/webhooks">
Get notified of events in real-time
<Card title="웹훅" icon="bell" href="/l/ko/developers/extend/capabilities/webhooks">
이벤트에 대한 실시간 알림을 받으세요
</Card>
<Card title="Apps" icon="puzzle-piece" href="/l/ko/developers/extend/capabilities/apps">
Build customizations as code (Alpha)
<Card title="" icon="puzzle-piece" href="/l/ko/developers/extend/capabilities/apps">
코드로 사용자 지정을 구축하세요(알파)
</Card>
</CardGroup>