ObjectOS
참조

CEL 표현식

수식, 술어, 스케줄, 템플릿 문자열에 사용되는 표현식 언어 — 다섯 가지 태그드 템플릿으로 제공됩니다.

CEL 표현식

ObjectOS는 작고 안전하며 샌드박스화된 표현식이 필요한 모든 곳에서 CEL(Common Expression Language)을 사용합니다. 수식 필드, 검증 규칙, 가시성 술어, 공유 조건, 플로우 가드, 스케줄, 템플릿 문자열 등이 여기에 해당합니다.

작성은 @objectstack/spec에서 가져오는 다섯 가지 태그드 템플릿을 통해 이루어집니다. 이들은 모두 런타임이 파싱하는 작은 JSON 객체를 생성합니다:

{ dialect: 'cel' | 'template' | 'cron', source: string }

스키마 소스: packages/spec/src/shared/expression.zod.ts.

다섯 가지 태그드 템플릿

템플릿Dialect용도예시
F`...`cel수식 필드 — 레코드와 함께 저장되는 파생 값F`record.amount * 0.1`
P`...`cel술어 — 검증 / 공유 / 가시성 / 조건을 위한 불리언P`record.status == "open"`
cel`...`cel일반 CEL — F나 P가 적합하지 않을 때(예: 파라미터 값)cel`now() + duration("P30D")`
tmpl`...`template{{var}} 보간을 사용하는 문자열 템플릿tmpl`Order from {{record.customer.name}}`
cron`...`cron스케줄 — 표준 5필드 cron 구문cron`0 9 * * 1-5`

평가 시점에서 F, P, cel 사이에는 기능적 차이가 없습니다 — 모두 CEL을 실행합니다. 이렇게 나눈 이유는 스키마(및 AI 에이전트)가 표현식이 담당하는 역할을 알 수 있도록 하고, 에디터가 타입 검사를 할 수 있도록 하기 위함입니다(수식은 값을 반환해야 하고, 술어는 불리언을 반환해야 합니다).

Import

import { F, P, cel, tmpl, cron } from '@objectstack/spec'

각각의 사용 위치

스펙의 필드태그예시 위치
Field.expression (formula 타입)F*.object.ts 수식 필드
Field.conditionalRequiredPobject 필드
Validation.predicatePobject 검증
SharingRule.conditionP공유 규칙
View.conditionalFormatting[].conditionP
Flow.step.when / Flow.transition.whenP플로우
Action.guardP액션
알림 제목 / 메시지 본문tmpl알림
Schedule.croncron예약된 플로우 / 리포트
임의의 값 파라미터cel플로우 단계 입력

변수 스코프

CEL 표현식은 다음과 같은 최상위 변수를 가진 컨텍스트에서 평가됩니다:

변수사용 가능한 경우내용
record거의 항상현재 평가 중인 레코드
previous업데이트 훅 / 변경 감지 시레코드의 변경 전 상태(또는 null)
input액션, 플로우 단계사용자가 제공한 입력 페이로드
os.user항상{ id, roles: string[], permissions: string[] }
os.org항상조직 / 테넌트 컨텍스트
os.env항상표현식에 노출되는 환경 변수

레거시 OLD / NEW 변수는 M9.5에서 제거되었습니다. previousrecord를 사용하세요.

표준 라이브러리

packages/formula/src/stdlib.ts에 등록되어 있습니다. 가장 많이 사용되는 빌트인:

시간

함수반환비고
now()Timestamp평가 컨텍스트에 고정됨 — 단일 쿼리 내에서 안정적
today()TimestampUTC 날짜의 시작
daysFromNow(int)Timestamp미래 날짜
daysAgo(int)Timestamp과거 날짜

CEL은 네이티브 timestamp(...), duration(...), date.getDayOfWeek() 등도 포함합니다 — CEL 스펙을 참고하세요.

유틸리티

함수목적
isBlank(x)null, undefined, "", 또는 빈 리스트이면 true
coalesce(a, b)첫 번째 non-null 값
trim(s)공백 제거
joinNonEmpty(list, sep)비어 있지 않은 항목들을 연결

네이티브 CEL 문자열 헬퍼(.contains(...), .startsWith(...), .matches(...), .size())는 항상 사용할 수 있습니다.

예시

수식 필드 — 라인 항목 합계:

{ name: 'subtotal', type: 'formula', expression: F`record.quantity * record.unit_price` }

검증 — 마감일은 오늘 이후여야 함:

{ message: 'Close date must be in the future', predicate: P`record.close_date > today()` }

가시성 — 매니저에게만 필드 표시:

{ visibleIf: P`'manager' in os.user.roles` }

플로우 가드 — 금액이 작을 때 단계 건너뛰기:

{ when: P`record.amount >= 1000` }

스케줄 — 평일 오전 9시:

{ schedule: cron`0 9 * * 1-5` }

템플릿 — 알림 제목:

{ subject: tmpl`[{{record.priority}}] {{record.subject}}` }

오류

표현식은 로드 시점에 컴파일됩니다. 실패는 소스 위치와 함께 VALIDATION_ERROR로 나타납니다:

{ "code": "VALIDATION_ERROR", "message": "CEL: unknown field 'amout' on Record", "details": { "field": "subtotal", "expression": "record.amout * 0.1" } }

잘못된 표현식은 조용히 실패하지 않습니다. 형식이 잘못되었거나 알 수 없는 필드를 참조하는 표현식은 위와 같이 위치가 표시된 메시지(철자가 틀린 필드에 대한 did-you-mean 힌트 포함)와 함께 os compile에서 실패합니다. 런타임에서는 잘못된 표현식이 조용히 null 또는 false로 평가되는 대신 출처가 명시된 오류를 던지므로, 수식 값이나 가드 결정이 조용히 손상되는 대신 로그와 감사 추적에서 실패를 확인할 수 있습니다.

참고

On this page