|
| 1 | +--- |
| 2 | +title: 액션(Actions) |
| 3 | +description: 플랫폼이 REST 엔드포인트, Console 버튼, 플로우 단계, AI 도구로 노출하는 명명된 작업 — 단 하나의 선언으로 제공됩니다. |
| 4 | +--- |
| 5 | + |
| 6 | +# 액션(Actions) |
| 7 | + |
| 8 | +**액션(Action)**은 객체에 대한 명명된 작업입니다. 한 번만 선언하면 |
| 9 | +다음과 같이 나타납니다: |
| 10 | + |
| 11 | +- `/api/v1/actions/<object>/<action>` 위치의 **REST 엔드포인트** |
| 12 | +- Console 레코드 상세 화면의 **버튼** |
| 13 | +- 자동화를 위한 **플로우 단계**(`type: 'action'`) |
| 14 | +- Agents와 AI Builder를 위한 **AI 도구**(`action_<name>`) |
| 15 | + |
| 16 | +네 가지 표면에 걸쳐 같은 작업을 반복하지 않습니다. 하나의 선언으로 네 가지 |
| 17 | +호출 방법을 제공합니다. |
| 18 | + |
| 19 | +## 액션 선언하기 |
| 20 | + |
| 21 | +```ts |
| 22 | +// src/actions/approve_invoice.action.ts |
| 23 | +import { Action } from '@objectstack/spec'; |
| 24 | + |
| 25 | +export const approveInvoice = Action.create({ |
| 26 | + name: 'approve_invoice', // lowercase snake_case (machine id) |
| 27 | + label: 'Approve Invoice', |
| 28 | + objectName: 'invoice', // attaches to the invoice object |
| 29 | + icon: 'check', |
| 30 | + variant: 'primary', |
| 31 | + locations: ['record_header'], // where the button shows |
| 32 | + confirmText: 'Approve this invoice?', |
| 33 | + successMessage: 'Invoice approved', |
| 34 | + refreshAfter: true, |
| 35 | + |
| 36 | + // collect input before running |
| 37 | + params: [ |
| 38 | + { name: 'note', label: 'Approval note', type: 'textarea' }, |
| 39 | + ], |
| 40 | + |
| 41 | + // only show the button when the record is still pending |
| 42 | + visible: 'record.status == "pending"', |
| 43 | + |
| 44 | + // what it does — a sandboxed script body |
| 45 | + type: 'script', |
| 46 | + body: { |
| 47 | + language: 'js', |
| 48 | + source: ` |
| 49 | + await ctx.data.update('invoice', input.id, { |
| 50 | + status: 'approved', |
| 51 | + approved_by: ctx.user.id, |
| 52 | + approved_at: now(), |
| 53 | + approval_note: input.note, |
| 54 | + }); |
| 55 | + `, |
| 56 | + }, |
| 57 | +}); |
| 58 | +``` |
| 59 | + |
| 60 | +`os dev`가 재컴파일한 후: |
| 61 | + |
| 62 | +- `POST /api/v1/actions/invoice/approve_invoice`가 동작합니다 |
| 63 | +- Console의 Invoice 레코드 페이지에 **Approve Invoice** 버튼이 표시됩니다 |
| 64 | +- 플로우에 `{ type: 'action', action: 'approve_invoice', inputs: { note: '…' } }`를 포함할 수 있습니다 |
| 65 | +- 스킬이 허용하는 경우 AI 어시스턴트가 `action_approve_invoice`를 호출할 수 있습니다 |
| 66 | + |
| 67 | +## 액션 유형 |
| 68 | + |
| 69 | +`type` 필드가 액션이 수행할 작업을 결정합니다: |
| 70 | + |
| 71 | +| `type` | 실행되는 것 | 사용 목적 | |
| 72 | +|---|---|---| |
| 73 | +| `script` | `body` — L1 formula 표현식 또는 샌드박스화된 L2 JavaScript | 대부분의 경우 — 서버 측 로직, 감사 가능 + AI 호출 가능 | |
| 74 | +| `api` | `target` 엔드포인트로의 HTTP 호출(`method`, `bodyExtra`) | 데이터 API 또는 플랫폼 엔드포인트 재사용 | |
| 75 | +| `flow` | `target`에 명명된 플로우를 실행 | 다단계 비즈니스 프로세스 | |
| 76 | +| `url` | `target` URL로 이동 | 딥 링크, 리디렉션 방식의 액션 | |
| 77 | +| `modal` | `target`에 명명된 페이지/모달을 열기 | 사용자 정의 대화 상자 | |
| 78 | +| `form` | `target`에 명명된 FormView를 열기 | 가이드 방식의 데이터 입력 | |
| 79 | + |
| 80 | +```ts |
| 81 | +// api type — reuse a data-API endpoint |
| 82 | +Action.create({ |
| 83 | + name: 'archive_order', |
| 84 | + objectName: 'order', |
| 85 | + label: 'Archive', |
| 86 | + locations: ['list_item'], |
| 87 | + type: 'api', |
| 88 | + method: 'PATCH', |
| 89 | + target: '/api/v1/data/order/{id}', |
| 90 | + bodyExtra: { archived: true }, |
| 91 | +}); |
| 92 | +``` |
| 93 | + |
| 94 | +`script`이 아닌 유형은 `target`이 필요합니다. 어떤 유형이든 액션은 모든 |
| 95 | +표면에서 동일한 일급 시민입니다. |
| 96 | + |
| 97 | +## 액션 호출하기 |
| 98 | + |
| 99 | +### REST |
| 100 | + |
| 101 | +```bash |
| 102 | +# the record id can go in the body, or in the path |
| 103 | +curl -X POST https://app.example.com/api/v1/actions/invoice/approve_invoice/inv_123 \ |
| 104 | + -H 'Authorization: Bearer <token>' \ |
| 105 | + -H 'Content-Type: application/json' \ |
| 106 | + -d '{"note": "LGTM"}' |
| 107 | +``` |
| 108 | + |
| 109 | +파라미터는 요청 본문에 평면 형태로 전송됩니다. 레코드 id는 후행 경로 세그먼트 |
| 110 | +(`.../approve_invoice/:recordId`)로 제공하거나 본문에 포함할 수 있습니다. 응답은 |
| 111 | +스크립트 본문의 반환 값(또는 `api` 유형 액션의 경우 호출 결과)입니다. |
| 112 | + |
| 113 | +### Console |
| 114 | + |
| 115 | +기본적으로 Console은 액션의 `visible` 조건으로 필터링하여 레코드 상세 페이지에 |
| 116 | +버튼으로 액션을 표시합니다. 뷰 설정에서 배치를 재정의할 수 있습니다: |
| 117 | + |
| 118 | +```ts |
| 119 | +defineView({ |
| 120 | + name: 'invoice_detail', |
| 121 | + object: 'invoice', |
| 122 | + actions: ['approve_invoice', 'reject_invoice', 'send_to_customer'], |
| 123 | +}); |
| 124 | +``` |
| 125 | + |
| 126 | +### 플로우에서 |
| 127 | + |
| 128 | +```ts |
| 129 | +{ |
| 130 | + type: 'action', |
| 131 | + action: 'approve_invoice', |
| 132 | + inputs: { note: 'Auto-approved by SLA flow' }, |
| 133 | + record: '{!trigger.record.id}', |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +### AI Agent에서 |
| 138 | + |
| 139 | +`approve_invoice`가 에이전트가 보유한 스킬에 포함되어 있으면 LLM이 이를 |
| 140 | +호출할 수 있습니다. 입력은 대화에서 가져오며, 권한은 사용자가 직접 호출한 |
| 141 | +것처럼 적용됩니다. |
| 142 | + |
| 143 | +> *"인보이스 INV-2042를 '전화로 확인됨'이라는 메모와 함께 승인해줘."* |
| 144 | +
|
| 145 | +## 권한 |
| 146 | + |
| 147 | +액션은 호출하는 사용자의 권한으로 실행됩니다. 플랫폼은 다음을 확인합니다: |
| 148 | + |
| 149 | +1. **객체 권한** — 사용자의 [권한 세트](/docs/configure/permissions/permission-sets)가 |
| 150 | + 액션에 필요한 객체 수준 접근 권한(예: 업데이트)을 부여해야 합니다. |
| 151 | +2. **필드 권한** — 액션이 쓰는 모든 필드에 대해 사용자가 쓰기 접근 권한(FLS)을 |
| 152 | + 가지고 있어야 합니다. |
| 153 | +3. **UI 게이팅** — `visible` 및 `disabled` 조건(CEL, `record`, `os.user`, |
| 154 | + 파라미터에 대해 평가됨)이 Console에서 버튼이 렌더링될지 또는 회색 처리될지를 |
| 155 | + 제어합니다. |
| 156 | + |
| 157 | +권한 확인에 실패하면 `PERMISSION_DENIED` 오류와 함께 `403`을 반환합니다. |
| 158 | + |
| 159 | +## 내장 액션 |
| 160 | + |
| 161 | +모든 객체는 다음을 기본으로 제공받습니다: |
| 162 | + |
| 163 | +| 액션 | 수행하는 작업 | |
| 164 | +|---|---| |
| 165 | +| `create` | 레코드 삽입 | |
| 166 | +| `update` | 레코드 업데이트 | |
| 167 | +| `delete` | 레코드 삭제(또는 소프트 삭제) | |
| 168 | +| `restore` | 소프트 삭제 취소 | |
| 169 | +| `clone` | 레코드 깊은 복사 | |
| 170 | +| `share` | 사용자 / 역할과 직접 공유 | |
| 171 | + |
| 172 | +이러한 액션은 다시 선언하지 마세요 — 객체의 [라이프사이클 및 기능 플래그](/docs/build/data-model)를 따릅니다. |
| 173 | + |
| 174 | +## 감사(Auditing) |
| 175 | + |
| 176 | +플랫폼 이벤트는 다음 필드를 포함하는 불변 기록인 `sys_audit_log`에 기록됩니다: |
| 177 | + |
| 178 | +- `user_id` — 작업을 시작한 사용자 |
| 179 | +- `action` — 액션 이름 |
| 180 | +- `object_name` 및 `record_id` — 변경된 대상 |
| 181 | +- `old_value` / `new_value` — 변경 내용 |
| 182 | +- `ip_address` / `user_agent` — 요청 출처 |
| 183 | +- `created_at` — 발생 시점 |
| 184 | + |
| 185 | +이는 *"누가 버튼을 눌렀는가?"*라는 질문에 가장 먼저 확인할 곳입니다. |
| 186 | + |
| 187 | +## AI Builder로 액션 생성하기 |
| 188 | + |
| 189 | +> *"`support_ticket`에 우선순위를 긴급으로 설정하고 온콜 엔지니어에게 |
| 190 | +> 할당하는 액션 `escalate_ticket`을 만들어줘."* |
| 191 | +
|
| 192 | +[AI Builder](/docs/build/ai-builder)는 액션 메타데이터를 생성하고 변경 사항을 |
| 193 | +승인 대기열에 넣습니다. 승인 후에는 REST, Console, 플로우, 그리고 — 재귀적으로 — |
| 194 | +AI 자체에서 액션을 호출할 수 있습니다. |
| 195 | + |
| 196 | +## 다음으로 갈 곳 |
| 197 | + |
| 198 | +- [플로우](/docs/build/flows) — 여러 액션을 비즈니스 로직으로 구성하기 |
| 199 | +- [Agents](/docs/build/agents) — 액션을 AI 도구로 노출하기 |
| 200 | +- [API 접근](/docs/configure/api-access) — 외부 시스템에서 액션 호출하기 |
| 201 | +- [권한](/docs/configure/permissions) — 누가 무엇을 호출할 수 있는지 제어하기 |
0 commit comments