snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
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 3s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
---
title: 수신 이메일 자동 회신
description: AI를 사용해 수신 이메일을 분류하고 스레드형 답장을 자동으로 보내는 워크플로를 구축하세요.
---
수신 이메일에 몇 시간이 아니라 몇 초 만에 응답하세요. 이 워크플로는 AI 에이전트를 사용해 잡음(뉴스레터, 스팸, 자동 회신)을 걸러내고 실제 메시지에는 개인화된 응답 초안을 작성합니다. 그런 다음 AI가 원본 대화 내에서 스레드형 답장으로 이를 전송합니다.
## 이메일 스레드 작동 방식
모든 이메일에는 숨겨진 `Message-ID` 헤더가 있으며, 이는 발신자의 메일 서버가 부여하는 고유한 식별자입니다. 이메일에 회신할 때, 메일 클라이언트는 해당 지문을 참조하는 `In-Reply-To` 헤더를 설정합니다. Gmail, Outlook 및 기타 모든 클라이언트가 메시지를 스레드로 묶는 방식입니다.
Twenty에서는 그 지문이 Message 객체의 `headerMessageId`로 저장됩니다. 워크플로우가 이를 가져와 Send Email 작업의 In-Reply-To 필드에 전달합니다.
## 워크플로우 구성
### 1단계: 새 워크플로우 생성
**설정 -> 워크플로우**로 이동한 다음 **+ 새 워크플로우**를 클릭하세요.
### 2단계: 수신 메시지에 트리거 설정
**When a Record is Created**를 선택하고 **Messages**를 선택하세요.
이메일이 Twenty에 도착할 때마다 이 트리거가 실행됩니다.
<img src="/images/user-guide/workflows/auto-reply/workflow-overview.png" style={{width:'100%'}} />
### 3단계: 발신자 찾기
**Search Records** 작업을 추가합니다.
발신자 주소는 메시지 자체에 있지 않고 — 연결된 Message Participant 레코드에 있습니다.
| 필드 | 값 |
| ------ | ------------------------------- |
| **객체** | 메시지 참가자 |
| **필터** | Message **값이** `{{trigger.id}}` |
| **필터** | Role **값이** From |
| **제한** | 1 |
이렇게 하면 발신자의 이메일은 `handle`에, 이름은 `displayName`에 담깁니다.
<img src="/images/user-guide/workflows/auto-reply/find-sender.png" style={{width:'100%'}} />
### 4단계: AI로 분류하고 응답 초안 작성
**AI Agent** 작업을 추가합니다. 이 단일 단계는 두 가지를 수행합니다: 이메일에 회신할지 여부를 결정하고, 필요하다면 회신 내용을 작성합니다.
다음과 같은 프롬프트를 사용하세요:
```
You are an email triage assistant for a sales team. Read the following
inbound email and decide if it deserves a reply.
Subject: {{trigger.subject}}
Body: {{trigger.text}}
From: {{Find Sender.first.displayName}} ({{Find Sender.first.handle}})
If this email is spam, a newsletter, an automated notification, or
otherwise does not need a human reply, respond with exactly: SKIP
Otherwise, write a short, professional reply (3-4 sentences max) that:
- Acknowledges their specific message
- Lets them know someone from the team will follow up shortly
- Is warm but not overly casual
Respond with only the reply text, no subject line or greeting prefix.
```
AI Agent는 다음 단계에서 참조할 수 있도록 `response` 필드에 응답을 출력합니다.
<img src="/images/user-guide/workflows/auto-reply/ai-triage.png" style={{width:'100%'}} />
### 5단계: AI 결정에 따라 분기
**If/Else** 작업을 추가하여 AI가 회신할지 건너뛸지를 확인합니다.
| 필드 | 값 |
| ----------- | ---------------------------------------- |
| **조건** | AI Agent `response`에 `SKIP`이 **포함되지 않음** |
| **참인 경우** | Send Email로 계속 |
| **그렇지 않으면** | 아무것도 하지 않음(워크플로우 종료) |
스팸, 뉴스레터, 자동 생성된 메시지는 제외됩니다. 그 외 모든 항목은 다음 단계로 이동합니다.
<img src="/images/user-guide/workflows/auto-reply/should-reply.png" style={{width:'100%'}} />
### 6단계: 스레드형 회신 보내기
"if true" 분기에서 **Send Email** 작업을 추가합니다. **Advanced options**를 클릭한 다음 **Add In-Reply-To**를 클릭합니다.
| 필드 | 값 |
| --------------- | -------------------------------------- |
| **받는 사람** | `{{Find Sender.first.handle}}` |
| **제목** | `Re: {{trigger.subject}}` |
| **Body** | `{{AI Triage & Draft Reply.response}}` |
| **In-Reply-To** | `{{trigger.headerMessageId}}` |
In-Reply-To 필드는 이 메시지를 새 대화가 아닌 회신으로 인식하게 합니다. 수신자는 Gmail, Outlook 등 모든 클라이언트에서 원본 이메일 아래에 스레드로 묶인 상태로 보게 됩니다.
<img src="/images/user-guide/workflows/auto-reply/send-email.png" style={{width:'100%'}} />
<Tip>
**In-Reply-To**에는 트리거에서 전달된 `message.headerMessageId`가 필요합니다 — 이는 수신자 주소가 아니라 이메일의 고유한 지문입니다. 비워 두면 이메일은 여전히 전송되지만 독립된 메시지로 보내집니다.
</Tip>
<Warning>
Gmail은 제목 줄을 사용하여 메시지를 스레드로 그룹화합니다. Gmail이 원래 스레드 내에 회신을 표시하려면 제목이 `Re:`(콜론과 공백 포함)로 시작해야 합니다. 이 표시가 없으면, In-Reply-To 헤더가 올바르게 설정되어 있어도 회신은 별도의 대화로 표시됩니다.
</Warning>
### 7단계: 테스트 및 활성화
**Test**를 클릭한 다음 메일 클라이언트를 확인하세요. 회신은 원본 메시지 아래에 스레드로 표시되어야 합니다.
만족스러우면 활성화하세요.
## 확장 아이디어
* **VIP에게만 회신** — 발신자의 도메인을 확인하거나 Twenty의 연락처로 존재하는지 확인하는 분기를 추가하세요
* **의도별 라우팅** — 영업 문의와 지원 요청을 다르게 처리하도록 별도의 AI Agent 프롬프트를 사용하세요
* **회신 전 정보 보강** — 보다 개인화된 회신을 위해 발신자의 회사나 거래 이력을 AI 프롬프트에 포함하도록 Search Records 단계를 추가하세요

View File

@@ -0,0 +1,179 @@
---
title: Closed Won 자동화
description: 기회가 성사되면 사후 작업을 자동화합니다.
---
거래가 성사되면 여러 작업이 필요합니다: 회사 상태 업데이트, 팀 구성원 알림, 온보딩 작업 생성. 워크플로우 하나로 이 모든 것을 자동화하세요.
## 문제
기회가 "Closed Won" 단계로 이동하면:
* 회사 유형을 "Prospect"에서 "Customer"로 변경해야 합니다
* 온보딩 작업을 생성해야 합니다
* Customer Success 팀에 알림을 보내야 합니다
* 영업 담당자에게 확인을 보내야 합니다
이 작업을 수동으로 하면 시간도 많이 들고 오류가 발생하기 쉽습니다.
## 해결책
성사 후 모든 활동을 자동으로 처리하는 워크플로우를 만드세요.
## 전체 워크플로우 설정
### 1단계: 워크플로우 생성
1. **설정 → 워크플로우**로 이동
2. **+ 새 워크플로우**를 클릭
3. 이름을 "Deal Won - Post-Win Automation"으로 지정
### 2단계: 트리거 구성
1. **레코드가 업데이트됨**을 선택
2. **Opportunities**를 선택
3. "Fields to monitor"에서 **Stage**를 선택
### 3단계: Stage 필터 추가
1. **필터** 액션을 추가합니다
2. 조건: `{{trigger.object.stage}}`이 "Closed Won"과 같음
### 4단계: 회사 유형 업데이트
1. **Update Record** 액션을 추가
2. 구성:
| 필드 | 값 |
| ----------- | ------------------------------- |
| **객체** | 회사 |
| **레코드** | `{{trigger.object.company.id}}` |
| **유형** | 고객 |
| **첫 거래 날짜** | `{{trigger.object.closedAt}}` |
| **계정 소유자** | `{{trigger.object.owner.id}}` |
### 5단계: 온보딩 작업 생성
1. **레코드 생성** 액션을 추가
2. 구성:
| 필드 | 값 |
| --------- | ------------------------------------------------------------------------------ |
| **객체** | 작업 |
| **제목** | `Onboarding: {{trigger.object.name}}` |
| **담당자** | Customer Success 팀 구성원 |
| **마감일** | 지금부터 3일 후 |
| **우선 순위** | 높음 |
| **관련 회사** | `{{trigger.object.company.id}}` |
| **관련 기회** | `{{trigger.object.id}}` |
| **설명** | `신규 고객 온보딩: {{trigger.object.company.name}}. 거래 금액: {{trigger.object.amount}}` |
### 6단계: Customer Success에 알림 보내기
1. 이메일 보내기 액션 추가
2. 구성:
| 필드 | 값 |
| --------- | -------------------------------------------------- |
| **받는 사람** | customer-success@yourcompany.com |
| **제목** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **본문** | 아래 예시를 참조하세요 |
**이메일 본문 예시**:
```
Hi CS Team,
We have a new customer!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Let's give them a great start!
```
### 7단계: 영업 담당자에게 확인 보내기
1. 또 하나의 **이메일 보내기** 액션 추가
2. 구성:
| 필드 | 값 |
| --------- | ---------------------------------------------------------------- |
| **받는 사람** | `{{trigger.object.owner.email}}` |
| **제목** | `✅ Deal Closed: {{trigger.object.name}}` |
| **본문** | 축하합니다! 거래가 처리되었습니다. Customer Success 팀에 알림이 전송되었으며 온보딩이 시작되었습니다. |
### 8단계: 테스트 및 활성화
1. 테스트용 기회를 "Closed Won"으로 이동하여 테스트하세요
2. 확인:
* 회사 유형이 "Customer"로 변경됨
* 온보딩 작업 생성됨
* CS 팀이 이메일을 받음
* 영업 담당자가 확인을 받음
3. 준비되면 활성화합니다
## Closed Lost 처리
실패한 거래에 대해서도 유사한 워크플로우를 만드세요:
### 트리거
* 레코드가 업데이트됨 (Opportunities, Stage = "Closed Lost")
### 작업
1. **레코드 생성**: "Lost Deal Analysis"용 작업
2. **레코드 업데이트**: 회사 레코드에 실패 사유 추가
3. **이메일 보내기**: 실패한 거래를 관리자에게 알림
## 고급: 다단계 온보딩
복잡한 온보딩의 경우 여러 작업을 생성합니다:
```javascript
export const main = async (params) => {
const tasks = [
{ title: "Welcome call", daysFromNow: 1, assignee: "CS" },
{ title: "Send onboarding materials", daysFromNow: 2, assignee: "CS" },
{ title: "Technical setup", daysFromNow: 5, assignee: "Support" },
{ title: "30-day check-in", daysFromNow: 30, assignee: "CS" }
];
return { tasks };
};
```
배열에서 각 작업을 생성하려면 **Iterator**를 사용하세요.
## 맞춤화 아이디어
### 다른 도구도 최신 상태로 유지하세요
* **HTTP Request**로 결제 시스템에 고객 생성
### 조건부 작업
**필터** 액션을 사용하여 다음을 수행:
* 엔터프라이즈와 SMB에 다른 온보딩 적용
* 지역에 따라 다른 담당자 지정
* 소액 거래는 알림 생략
### 거래 세부 정보 포함
형식을 지정하려면 **Code** 액션을 사용하세요:
* 거래 요약 문서
* CS 팀을 위한 인수인계 노트
* 맞춤형 온보딩 체크리스트
## 관련 항목
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)
* [워크플로우에서 이메일 보내기](/l/ko/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,136 @@
---
title: 오래된 기회 감지
description: 기회가 업데이트되지 않았을 때 관리자에게 자동으로 알립니다.
---
기회가 오래되면 관리자에게 알림을 보내 파이프라인을 건강하게 유지하세요. 이 워크플로우는 지정한 일수 동안 업데이트되지 않은 기회를 확인합니다.
## 문제
업데이트 없이 방치된 기회는 다음을 초래합니다:
* 거래가 식어감
* 신뢰할 수 없는 매출 예측
* 매출 손실
## 해결책
오래된 기회를 찾아 해당 관리자에게 이메일을 보내는 예약 워크플로우를 만듭니다.
## 단계별 설정
### 1단계: 워크플로우 생성
1. **설정 → 워크플로우**로 이동
2. **+ 새 워크플로우**를 클릭
3. 이름을 "오래된 기회 경고"로 지정
### 2단계: 트리거 구성
1. **On a Schedule**를 선택
2. 매일 실행되도록 설정합니다(예: 매일 오전 8시).
### 3단계: 오래된 기회 검색
1. **레코드 검색** 액션을 추가합니다
2. 구성:
| 필드 | 값 |
| ------ | ------------------------------------------ |
| **객체** | 기회 |
| **필터** | Updated At이 (오늘 - 7일) 이전 |
| **필터** | Stage가 "Closed Won"이 아니며 "Closed Lost"도 아님 |
| **제한** | 100 |
### 4단계: 발견된 항목이 있는지 확인
1. **필터** 액션을 추가합니다
2. 조건: `{{searchRecords.length}}`가 0보다 큼
3. 오래된 기회가 없으면 워크플로우는 여기서 중지됩니다
### 5단계: 경고 형식 지정(코드 액션)
이메일을 형식 지정하기 위해 **코드** 액션을 추가합니다:
```javascript
export const main = async (params) => {
const opportunities = params.opportunities;
// Group opportunities by owner
const byOwner = {};
opportunities.forEach(opp => {
const ownerEmail = opp.owner?.email || 'unassigned';
if (!byOwner[ownerEmail]) {
byOwner[ownerEmail] = [];
}
byOwner[ownerEmail].push({
name: opp.name,
amount: opp.amount,
lastUpdated: opp.updatedAt,
stage: opp.stage
});
});
// Format summary for manager
let summary = "Stale Opportunities Report\n\n";
Object.entries(byOwner).forEach(([owner, opps]) => {
summary += `${owner}: ${opps.length} stale opportunities\n`;
opps.forEach(opp => {
summary += ` - ${opp.name} (${opp.stage})\n`;
});
summary += "\n";
});
return {
summary,
totalCount: opportunities.length
};
};
```
### 6단계: 경고 이메일 보내기
이메일 보내기 액션 추가:
| 필드 | 값 |
| --------- | -------------------------------------------- |
| **받는 사람** | sales-manager@yourcompany.com |
| **제목** | `🚨 {{code.totalCount}}건의 오래된 기회에 주의가 필요합니다` |
| **본문** | `{{code.summary}}` |
### 7단계: 테스트 및 활성화
1. 워크플로우를 실행하려면 **Test**를 클릭합니다
2. 이메일에 올바른 데이터가 포함되어 있는지 확인합니다
3. 준비되면 활성화합니다
## 맞춤 설정 옵션
### 정체 임계값 변경
레코드 검색 필터를 수정하여 7일을 원하는 기간으로 변경합니다:
* 고속 영업의 경우 3일
* 엔터프라이즈 거래의 경우 14일
* 긴 영업 주기의 경우 30일
### 개별 영업 담당자 알림
관리자에게 보내는 단일 이메일 대신 **Iterator**를 사용하여 각 영업 담당자에게 자신의 오래된 거래에 대한 개인화된 이메일을 보냅니다.
### 에스컬레이션 추가
심각도가 점차 높아지도록 여러 개의 워크플로우를 만듭니다:
1. 7일차: 영업 담당자에게 이메일
2. 14일차: 영업 담당자 및 관리자에게 이메일
3. 21일차: 관리자가 개입할 수 있도록 작업 생성
### Slack에 포함
이메일 대신 또는 추가로 **HTTP Request**를 사용해 Slack 웹훅으로 게시합니다.
## 관련 항목
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)
* [워크플로우에서 이메일 보내기](/l/ko/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,69 @@
---
title: 수신한 이메일 수 표시
description: 각 연락처로부터 받은 이메일 수를 자동으로 집계해 표시하는 워크플로우를 만듭니다.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/workflow-number-of-emails-received.png" style={{width:'100%'}} />
## 개요
이 워크플로우는 새 이메일을 받을 때마다 트리거되며, 해당 발신자로부터 받은 이메일의 총 개수를 사람 레코드의 사용자 지정 필드에 업데이트합니다.
## 사전 준비
이 워크플로우를 설정하기 전에 **사람** 객체에 사용자 지정 필드를 생성합니다:
1. **설정 → 데이터 모델 → 사람**으로 이동
2. 새 **숫자** 필드를 추가합니다
3. 필드 이름을 "이 사람에게서 받은 이메일 수"처럼 지정합니다
## 단계별 설정
<VimeoEmbed videoId="1148700336" title="동영상 시연" />
### 1단계: 트리거 구성
1. **워크플로우**로 이동해 새 워크플로우를 생성합니다
2. 트리거로 **레코드가 생성됨**을 선택합니다
3. **Message Participants**를 선택합니다(고급 객체에 있음)
<Note>
Message Participant는 메시지 ID와 사람 ID의 조합으로, 메시지마다 하나의 고유 레코드를 생성합니다. 이는 발신자(또는 수신자)의 이메일 주소가 들어 있는 `handle` 필드에 접근할 수 있으므로 메시지를 직접 추적하는 것보다 더 쉽습니다.
</Note>
### 2단계: Role 기준으로 필터링
1. **필터** 액션을 추가합니다
2. 조건을 설정합니다: **Role**이 **FROM**과 같음
이렇게 하면 해당 사람이 보낸 메시지만 집계되고, 그 사람에게 보낸 메시지는 제외됩니다.
### 3단계: 동일한 Handle을 가진 모든 Message Participants 검색
1. **레코드 검색** 액션을 추가합니다
2. 객체로 **Message Participants**를 선택합니다
3. 필터를 추가합니다: **Handle**이 트리거의 handle(발신자의 이메일 주소)과 같고, **Role**이 **FROM**과 같음
4. **Limit**을 1에서 최대값인 **200**으로 늘립니다
해당 이메일 주소에서 보낸 모든 메시지를 찾아 총 개수를 구합니다.
<Note>
Search Records 액션은 최대 200개의 레코드만 반환합니다. 그러나 개별 레코드가 아니라 `totalCount` 값만 사용하므로, 이 단계는 해당 사람이 보낸 이메일의 총 개수를 반환합니다.
</Note>
### 4단계: Create or Update Record 액션으로 사람 레코드를 업데이트합니다
1. **레코드 생성 또는 업데이트** 액션을 추가합니다
2. 객체로 **People**를 선택합니다
3. Message Participant의 `handle`과 해당 사람의 이메일을 일치시켜 사람을 찾습니다.
4. 사용자 지정 "수신한 이메일 수" 필드를 `{{searchRecords.totalCount}}`로 설정합니다
Search Records 액션의 `totalCount` 값은 이 사람에게서 받은 이메일의 총 개수를 나타냅니다.
## 관련 항목
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)
* [사용자 지정 필드 만들기](/l/ko/user-guide/data-model/how-tos/customize-your-data-model)
* [레코드 검색 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions#search-records)

View File

@@ -0,0 +1,170 @@
---
title: 관련 레코드 데이터 표시
description: "워크플로우를 사용해 관련 레코드의 데이터를 표시합니다(예: Opportunities에 회사 정보 표시)."
---
관련 레코드의 데이터를 내 레코드에 직접 표시하세요 — 예를 들어, Company의 직원 수를 해당 Opportunities에 표시합니다. 중첩 필드가 기본적으로 제공될 때까지 이 워크플로우 우회 방법이 유용합니다.
## 일반적인 사용 사례
| 소스 | 대상 | 복사할 필드 |
| -- | -- | ------------------ |
| 회사 | 기회 | 산업, 회사 규모, ARR |
| 사람 | 기회 | 이메일, 전화, 직함 |
| 기회 | 회사 | 최근 거래 금액, 최근 성사 일자 |
## 기본 필드 복사
### 예시: 연락처 이메일을 Opportunity로 복사
**목표**: Opportunity에서 담당자를 설정할 때, 쉽게 액세스할 수 있도록 해당 이메일을 Opportunity로 복사합니다.
### 사전 준비
워크플로우를 만들기 전에 **설정 → 데이터 모델 → Opportunities**에서 대상 필드를 생성하세요:
* 연락처 이메일(유형: Email)
* 연락처 전화(유형: Phone)
### 설정
1. **트리거**: 레코드가 업데이트됨 (Opportunities, 담당자 필드)
2. **필터**: 담당자가 비어 있지 않은지 확인
3. **레코드 검색**: 연결된 사람 찾기
* 객체: People
* 필터: ID가 `{{trigger.object.pointOfContact.id}}`와 같음
4. **레코드 업데이트**:
* 객체: Opportunities
* 레코드: `{{trigger.object.id}}`
* 연락처 이메일: `{{searchRecords[0].email}}`
* 연락처 전화: `{{searchRecords[0].phone}}`
## 여러 필드 복사
### 예시: 모든 관련 Opportunities에 회사 정보 동기화
**목표**: 회사 세부 정보가 변경되면 관련된 모든 Opportunities를 업데이트합니다.
### 설정
1. **트리거**: 레코드가 업데이트됨 (Companies)
* 필드: 산업, 회사 규모, 연간 매출
2. **레코드 검색**: 이 회사의 모든 Opportunities 찾기
* 객체: Opportunities
* 필터: 회사 ID가 `{{trigger.object.id}}`와 같음
3. **Iterator**: 각 Opportunity를 순회
4. **레코드 업데이트** (iterator 내부):
* 객체: Opportunities
* 레코드: `{{iterator.currentItem.id}}`
* 회사 산업: `{{trigger.object.industry}}`
* 회사 규모: `{{trigger.object.companySize}}`
* 회사 ARR: `{{trigger.object.annualRevenue}}`
## 레코드 생성 시 복사
### 예시: 회사 데이터로 Opportunity를 미리 채우기
**목표**: 회사와 연결된 Opportunity를 생성할 때 핵심 회사 정보를 자동으로 복사합니다.
### 사전 준비
**설정 → 데이터 모델 → Opportunities**에서 대상 필드를 생성하세요:
* 회사 산업(유형: Text)
* 회사 규모(유형: Number)
### 설정
1. **트리거**: 레코드가 생성됨 (Opportunities)
* 필터: 회사가 비어 있지 않음
2. **레코드 검색**: 연결된 회사의 세부 정보 가져오기
* 객체: Companies
* 필터: ID가 `{{trigger.object.company.id}}`와 같음
3. **레코드 업데이트**:
* 객체: Opportunities
* 레코드: `{{trigger.object.id}}`
* 회사 산업: `{{searchRecords[0].industry}}`
* 회사 규모: `{{searchRecords[0].employees}}`
<Note>
**작업 및 노트 제한 사항**: 작업과 노트의 관계는 다대다로 하드코딩되어 있으며, 워크플로우 트리거 또는 액션에서 아직 사용할 수 없습니다. 이러한 관계에 액세스하려면 대신 [API](/l/ko/developers/extend/api)를 사용하세요.
</Note>
## 양방향 동기화
### 예시: 기본 담당자 동기화 유지
**목표**: 회사의 기본 담당자가 변경되면 담당자를 업데이트합니다. 사람이 기본 담당자가 되면 회사를 업데이트합니다.
### 워크플로우 1: Company → Person
1. **트리거**: 레코드가 업데이트됨 (Companies, 기본 담당자 필드)
2. **레코드 업데이트**: 사람의 "Is Primary Contact"를 true로 설정
3. **레코드 검색**: 이전 기본 담당자 찾기
4. **레코드 업데이트**: 이전 담당자의 "Is Primary Contact"를 false로 설정
### 워크플로우 2: Person → Company
1. **트리거**: 레코드가 업데이트됨 (People, Is Primary Contact = true)
2. **레코드 업데이트**: 회사의 기본 담당자를 이 사람으로 설정
<Note>
무한 루프를 피하기 위해 양방향 동기화에 주의하세요. 업데이트하기 전에 값이 실제로 변경되었는지 필터로 확인하세요.
</Note>
## 복잡한 매핑에 코드 사용
### 예시: 복사 중 데이터 변환
**목표**: 사람의 전화번호를 Opportunity로 복사하고 형식을 지정합니다.
```javascript
export const main = async (params) => {
const { phone } = params;
if (!phone) return { formattedPhone: null };
// Remove non-numeric characters
const digits = phone.replace(/\D/g, '');
// Format as (XXX) XXX-XXXX
const formatted = digits.length === 10
? `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`
: phone;
return { formattedPhone: formatted };
};
```
## 모범 사례
### 루프 방지
* 서로를 끝없이 트리거하는 워크플로우를 만들지 마세요
* 특정 필드 조건 사용
* 값이 실제로 변경되었는지 확인을 추가하세요
### 누락된 데이터 처리
* 복사하기 전에 소스 레코드가 존재하는지 항상 확인하세요
* 선택적 필드에 기본값 제공
* 소스 필드가 비어 있을 때 건너뛰도록 필터 사용
### 성능
* 여러 레코드에 복사할 때 일괄 업데이트 사용
* 대량 동기화 작업에는 예약된 워크플로우를 사용하세요
* 다수의 레코드를 업데이트할 때 Iterator 사용을 고려하세요
## 관련 항목
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)
* [워크플로우 트리거](/l/ko/user-guide/workflows/capabilities/workflow-triggers)

View File

@@ -0,0 +1,202 @@
---
title: 수식 필드
description: 네이티브 지원이 제공될 때까지 워크플로우를 사용해 수식 필드를 생성하세요.
---
Twenty는 아직 네이티브 수식 필드를 지원하지 않습니다(2026년에 제공 예정이지만), 워크플로우를 사용하면 동일한 결과를 얻을 수 있습니다. 이 우회 방법을 사용하면 단순한 연결부터 복잡한 비즈니스 로직까지 필드 값을 자동으로 계산하고 채울 수 있습니다.
## 일반적인 사용 사례
| 사용 사례 | 수식 예시 |
| --------------- | ---------------------------- |
| **전체 이름** | First Name + " " + Last Name |
| **예상 금액** | Amount × Probability |
| **마감일까지 남은 일수** | Due Date - Today |
| **단계 경과 일수** | Today - Stage Entry Date |
| **리드 점수** | 여러 기준을 기반으로 한 점수 |
<Tip>
파이프라인 단계에서 시간을 추적하는 전체 예시는 [각 단계에서 기회가 머무는 기간 추적하기](/l/ko/user-guide/views-pipelines/how-tos/track-time-in-stage)를 참조하세요.
</Tip>
## 기본 수식: 연결
### 예시: 전체 이름 자동 채우기
**목표**: 이름과 성을 결합해 전체 이름 필드에 자동으로 입력합니다.
### 설정
1. **트리거**: 레코드가 업데이트되거나 생성됨 (People)
2. **필터**: 이름 또는 성이 변경되었는지 확인
3. **코드 액션**:
```javascript
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
```
4. **레코드 업데이트**: Full Name을 `{{code.fullName}}`(으)로 설정
## 수치 수식: 예상 금액
### 예시: 예상 매출 계산
**목표**: 기회 금액에 확률을 곱하여 예상 금액을 계산합니다.
전체 워크플로우는 [파이프라인에서 예상 금액 표시하기](/l/ko/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)를 참조하세요.
### 설정
1. **트리거**: 레코드가 업데이트됨 (Opportunities, Amount 또는 Probability 필드)
2. **코드 액션**:
```javascript
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
```
3. **레코드 업데이트**: Expected Amount를 `{{code.expectedAmount}}`(으)로 설정
## 날짜 수식: 일수 계산
### 예시: 작업 마감일까지 남은 일수
**목표**: 작업 마감일까지 남은 일수를 계산합니다.
### 설정
1. **트리거**: 레코드가 업데이트되거나 생성됨 (Tasks, Due Date 필드)
2. **코드 액션**:
```javascript
export const main = async (params) => {
const { dueDate } = params;
if (!dueDate) {
return { daysUntilDue: null };
}
const due = new Date(dueDate);
const today = new Date();
const diffTime = due - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return { daysUntilDue: diffDays };
};
```
3. **레코드 업데이트**: Days Until Due를 `{{code.daysUntilDue}}`(으)로 설정
<Note>
음수 값은 기한이 지난 작업을 의미합니다. 이 필드를 사용하여 긴급도에 따라 작업을 필터링하거나 정렬할 수 있습니다.
</Note>
## 조건 수식: 리드 점수
### 예시: 기준에 따라 리드 점수 계산
**목표**: 회사 규모, 산업, 참여도에 따라 리드 점수를 산정합니다.
### 설정
1. **트리거**: 레코드가 업데이트됨 (People 또는 Companies)
2. **코드 액션**:
```javascript
export const main = async (params) => {
const { companySize, industry, hasEmail, hasPhone, source } = params;
let score = 0;
// Company size scoring
if (companySize === 'Enterprise') score += 30;
else if (companySize === 'Mid-Market') score += 20;
else if (companySize === 'SMB') score += 10;
// Industry scoring
const targetIndustries = ['Technology', 'Finance', 'Healthcare'];
if (targetIndustries.includes(industry)) score += 25;
// Contact info scoring
if (hasEmail) score += 10;
if (hasPhone) score += 15;
// Source scoring
if (source === 'Referral') score += 20;
else if (source === 'Website') score += 10;
return { leadScore: score };
};
```
3. **레코드 업데이트**: Lead Score를 `{{code.leadScore}}`(으)로 설정
## 텍스트 수식: 도메인 추출
### 예시: 이메일에서 도메인 추출
**목표**: 이메일 도메인을 자동으로 추출하여 저장합니다.
### 설정
1. **트리거**: 레코드가 업데이트됨 (People, Email 필드)
2. **코드 액션**:
```javascript
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
```
3. **레코드 업데이트**: Domain 필드를 `{{code.domain}}`(으)로 설정
## 모범 사례
### 성능
* 관련 필드 변경에만 트리거되도록 하세요.
* 계산이 필요 없는 레코드는 필터로 건너뛰세요.
* 대량 워크플로우에서는 복잡한 계산을 피하세요.
### 오류 처리
* 계산 전에 null/undefined 값을 확인하세요.
* 데이터가 없을 때는 기본값을 사용하세요.
* 계산이 실패하면 명확한 오류 메시지를 반환하세요.
### 테스트
* 엣지 케이스로 테스트하세요(빈 필드, 0 값 등).
* 활성화 전에 계산을 수동으로 검증하세요.
* 예상치 못한 결과가 있는지 워크플로우 실행을 모니터링하세요.
## 관련 항목
* [파이프라인에서 예상 금액 표시하기](/l/ko/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)
* [단계에서 시간 추적하기](/l/ko/user-guide/views-pipelines/how-tos/track-time-in-stage)
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,127 @@
---
title: 검토할 노트에 대해 팀원에게 알림 보내기
description: 노트 검토를 위해 동료를 지정할 때 자동 이메일 알림을 보냅니다.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<Note>
**노트에서 팀원을 직접 태그하는 기능은 아직 지원되지 않습니다.** 이 기능은 2026년 하반기에 제공될 예정입니다. 그동안 이 워크플로우가 실질적인 대안을 제공합니다.
</Note>
## 과제
노트에서 동료를 태그해 검토 알림을 받도록 하고 싶습니다. 현재 Twenty는 노트나 작업에서 @멘션을 지원하지 않습니다.
## 대안 방법
노트 본문에서 태그하는 대신 다음을 수행할 수 있습니다:
1. 노트 개체에 **검토자** 관계 필드를 생성합니다
2. 노트를 생성하거나 편집할 때 검토자를 지정합니다
3. 워크플로우가 검토자에게 노트 링크가 포함된 이메일을 자동으로 보내도록 설정하세요
<Warning>
**제한 사항**: 이 방법은 필드당 한 명의 검토자만 지정할 수 있습니다. 여러 사람에게 알림이 필요하다면 추가 관계 필드를 생성하세요(예: 검토자 1, 검토자 2). 한 번에 많은 공동 작업자를 태그해야 하는 경우 확장성이 떨어집니다.
</Warning>
## 단계별 설정
### 1단계: 검토자 필드 생성
<img src="/images/user-guide/workflows/reviewer-field-for-note.png" style={{width:'100%'}} />
1. **설정 → 데이터 모델 → 노트**로 이동
2. **필드 추가**를 클릭합니다
3. 구성:
| 설정 | 값 |
| --------- | ---------------------- |
| **Name** | 검토자 |
| **유형** | 관계 |
| **관련 개체** | 워크스페이스 멤버 |
| **관계 유형** | 여러 노트 → 하나의 워크스페이스 구성원 |
4. 필드를 저장합니다
### 2단계: 워크플로우 생성
<VimeoEmbed videoId="1151897168" title="동영상 시연" />
#### 워크플로우 트리거 만들기
1. 메인 화면으로 이동해 새 워크플로우를 생성합니다
2. 트리거로 **레코드가 업데이트됨**을 선택합니다
3. 개체로 **노트**를 선택하고 검토자 필드를 지정합니다.
4. 노트의 검토자가 업데이트될 때마다 트리거됩니다.
#### 방금 검토자가 지정되었는지 확인하는 필터 추가
검토자가 지정된 경우에만 워크플로우가 계속되도록 **필터** 작업을 추가합니다:
| 조건 | 값 |
| ------- | -------- |
| **검토자** | 비어 있지 않음 |
#### 워크스페이스 구성원 검색
1. **레코드 검색** 작업을 추가합니다
2. 고급 객체 아래의 **Workspace Member**를 선택합니다
3. 필터 추가:
| 조건 | 값 |
| ------ | ------------------------- |
| **ID** | `{{note.reviewerId}}`와 같음 |
이렇게 하면 이메일 주소와 이름을 포함한 전체 워크스페이스 구성원 레코드를 가져옵니다.
#### 알림 이메일 보내기
1. **이메일 보내기** 작업을 추가합니다
2. 구성:
| 필드 | 값 |
| --------- | ---------------------------------- |
| **받는 사람** | 레코드 검색 → 첫 번째 워크스페이스 구성원 → 사용자 이메일 |
| **제목** | 노트 검토가 필요합니다 |
| **본문** | 아래 예시를 참조하세요 |
**이메일 본문 예시:**
```text
Hi {{searchRecord.firstWorkspaceMember.name.firstName}},
You've been assigned to review a note.
View the note here:
https://yourSubDomain.twenty.com/object/note/{{recordIsUpdated.id}}
Best,
Twenty
```
<Note>
`yourSubDomain`을 실제 Twenty 워크스페이스 서브도메인으로 바꾸세요.
</Note>
#### 테스트 및 활성화
1. 워크플로우를 수동으로 실행하려면 **Test**를 클릭합니다
2. 노트를 생성하거나 편집하고 검토자를 지정합니다
3. 검토자의 받은편지함에서 알림 이메일을 확인합니다
4. 워크플로우를 활성화합니다
## 사용법
설정이 완료되면 팀은 다음과 같이 이 워크플로우를 사용할 수 있습니다:
1. 새 노트를 생성(또는 기존 노트를 편집)합니다
2. **검토자** 필드에서 동료를 선택합니다
3. 동료는 노트로 바로 이동하는 링크가 포함된 이메일을 자동으로 받습니다
## 관련 항목
* [워크플로우 트리거](/l/ko/user-guide/workflows/capabilities/workflow-triggers)
* [워크플로우에서 이메일 보내기](/l/ko/user-guide/workflows/capabilities/send-emails-from-workflows)
* [사용자 지정 필드 만들기](/l/ko/user-guide/data-model/how-tos/create-custom-fields)
* [관계 필드 만들기](/l/ko/user-guide/data-model/how-tos/create-relation-fields)

View File

@@ -0,0 +1,106 @@
---
title: 마감 예정 작업에 대한 이메일 알림 보내기
description: 예정되었거나 기한이 지난 작업에 대해 팀 구성원에게 자동으로 알립니다.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/email_tasks_due.png" style={{width:'100%'}} />
오늘 마감인 작업에 대해 각 팀원에게 매일 이메일 알림을 보냅니다.
## 개요
이 워크플로우는 일정에 따라 실행되며 다음을 수행합니다:
1. 모든 워크스페이스 구성원을 가져옵니다
2. 각 구성원을 순회합니다
3. 각 구성원의 오늘 마감 작업을 찾습니다
4. 개인화된 이메일을 작성해 전송합니다
## 단계별 설정
<VimeoEmbed videoId="1147463514" title="동영상 시연" />
### 1단계: 트리거 구성
1. **Settings → Workflows**로 이동해 새 워크플로우를 생성합니다
2. 트리거로 **On a Schedule**를 선택합니다
3. 매일 오전 8시에 실행되도록 다음 cron 표현식을 사용하세요: `0 8 * * *`
### 2단계: 모든 워크스페이스 구성원 검색
1. **레코드 검색** 액션을 추가합니다
2. 고급 객체(advanced objects) 아래의 **Workspace Members**를 선택합니다
3. 필터는 필요 없습니다 — 모든 구성원이 반환됩니다
### 3단계: 이터레이터 추가
1. **Iterator** 액션을 추가합니다
2. 입력 배열을 이전 단계의 워크스페이스 구성원으로 설정합니다
3. 이터레이터 내부의 모든 액션은 구성원당 한 번씩 실행됩니다
### 4단계: 오늘 마감 작업 검색(이터레이터 내부)
1. 이터레이터 내부에 **레코드 검색** 액션을 추가합니다
2. 객체로 **Tasks**를 선택합니다
3. 필터 추가:
* **Assignee** = 현재 워크스페이스 구성원(이터레이터에서 가져옴)
* **Due Date** = 오늘
### 5단계: 작업을 이메일 본문 형식으로 정리(이터레이터 내부)
작업을 링크가 포함된 읽기 쉬운 목록으로 형식화하기 위해 **Code** 액션을 추가합니다:
```javascript
export const main = async (params: {
tasksDue?: Array<{ id: string; title: string }> | null | string;
}) => {
const tasksDue =
typeof params.tasksDue === "string"
? JSON.parse(params.tasksDue)
: params.tasksDue;
if (!Array.isArray(tasksDue) || tasksDue.length === 0) {
return {
formattedTasks: "No tasks due today."
};
}
const formattedTasks = tasksDue
.map(
t =>
`${t.title}\nhttps://yourSubDomain.twenty.com/object/task/${t.id}`
)
.join("\n\n");
return { formattedTasks };
};
```
<Note>
`yourSubDomain`을 실제 Twenty 워크스페이스 서브도메인으로 바꾸세요.
</Note>
### 6단계: 이메일 보내기(이터레이터 내부)
1. **Send Email** 액션을 추가합니다(여전히 이터레이터 내부)
2. 구성:
| 필드 | 값 |
| --------- | ------------------------------------------------------ |
| **받는 사람** | `{{iterator.currentItem.userEmail}}` (워크스페이스 구성원의 이메일) |
| **제목** | 오늘 마감인 작업 |
| **본문** | `{{code.formattedTasks}}` |
### 7단계: 테스트 및 활성화
1. 워크플로우를 수동으로 실행하려면 **Test**를 클릭합니다
2. 이메일이 도착했는지 받은편지함을 확인하세요
3. 워크플로우를 활성화합니다
## 관련 항목
* [워크플로우 액션](/l/ko/user-guide/workflows/capabilities/workflow-actions)
* [워크플로우에서 이메일 보내기](/l/ko/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Code 액션에서 배열 처리하기](/l/ko/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)