값 표현식 (Value Expressions)
최종 수정: 2026년 7월 20일
Script에서 값이 필요한 모든 자리(URL, 요청 body, 필드 값, 조건, 필터 값, 대상 id 등)는 아래 세 형태 중 하나입니다. 이 문서는 그 세 형태와, 값이 어디서 오는지(컨텍스트 루트), 그리고 WEEGLOO 데이터 특유의 로케일 맵 규칙을 설명합니다. Statement 카탈로그의 모든 필드가 이 규칙을 따릅니다.
세 가지 형태
| 형태 | 규칙 | 예 |
|---|---|---|
| 참조 (reference) | 문자열 안의 { /json-pointer }를 컨텍스트에서 resolve합니다. | "{ /payload/fields/title }" |
| 리터럴 (literal) | { /ptr }가 없는 값(문자열, 숫자, 불리언, 객체, 배열)입니다. 그대로 사용합니다. | "draft", 42, true, { "a": 1 } |
| 연산과 조건 (JsonLogic) | 연산자 하나를 키로 갖는 객체입니다. 피연산자는 다시 값 표현식(참조, 리터럴, 중첩)입니다. | { "+": [ "{ /vars/n }", 1 ] } |
세 형태는 중첩됩니다. JsonLogic 피연산자에 참조를, 참조 결과를 다시 연산에 넣는 식으로 조합합니다.
참조: { /json-pointer }
중괄호 안에 RFC 6901 JSON Pointer(반드시 /로 시작)를 넣습니다. 중괄호 주변 공백은 허용됩니다({ /a/b }는 {/a/b}와 같습니다).
단일 포인터와 혼합 템플릿: 타입 규칙
- 문자열 전체가 단일 포인터면 그 값의 원래 타입을 그대로 유지합니다(숫자면 숫자, 객체면 객체, 배열이면 배열).
- 리터럴 텍스트와 섞이면 문자열로 연결(concatenation) 됩니다.
"{ /payload/fields/count }" // 숫자 값이면 숫자 그대로 (예: 42)
"{ /payload/fields/tags }" // 배열이면 배열 그대로
"page-{ /payload/fields/n }-of-10" // 문자열 연결 → "page-42-of-10"
"Bearer { /payload/fields/token }" // 문자열 연결 → "Bearer abc123"없는 값과 이스케이프
- 경로가 없거나 값이 비면 단일 포인터는
null, 혼합 템플릿은 빈 문자열로 처리됩니다. {를 리터럴로 쓰려면\{로 이스케이프합니다(그 위치는 포인터로 해석되지 않습니다).
컨텍스트 루트: 값은 어디서 오나
{ /pointer }의 최상위 세그먼트는 아래 다섯 중 하나입니다.
| 루트 | 내용 |
|---|---|
/payload | 호출 시 전달된 JSON payload(입력)입니다. 예: { /payload/fields/email } |
/headers | 호출 시 전달된 요청 HTTP 헤더입니다. 키는 소문자이고 이름당 단일 값입니다. 예: { /headers/authorization } |
/<name> | name이 붙은 앞선 statement의 결과입니다. 예: { /order/sys/id } |
/vars/<name> | SetVar로 선언한 script-scoped 가변 변수입니다. 예: { /vars/total } |
/error | Try의 catch 블록 안에서만 씁니다. 잡힌 에러 { message, statement }입니다. 예: { /error/message } |
statement 결과의 모양
name을 붙인 statement의 결과 형태는 타입마다 다릅니다.
| statement | 결과 모양 | 참조 예 |
|---|---|---|
Http | { status, body } | { /resp/status }, { /resp/body/choices/0/message/content } |
ResourceCreate, ResourceRead(단건), ResourceFind(단건) | 리소스 그 자체 | { /post/sys/id }, { /post/fields/title/en-US } |
ResourcePageRead | { items, next } | { /page/items/0/sys/id }, { /page/next } |
ResourceFind는 매치가 없으면null을 바인딩합니다.{ "==": [ "{ /found }", null ] }로 존재 여부를 분기합니다.ResourceRead(단건)는 대상이 없으면 에러입니다(Try로 처리 가능). 자세한 내용은 Statement 카탈로그의 리소스 읽기에서 다룹니다.
연산과 조건: JsonLogic
계산이나 조건이 필요하면 jsonlogic.com 스펙의 연산자 객체를 씁니다.
- 데이터 접근은 vanilla
var(dot-path)가 아니라{ /ptr }참조로 통일합니다. 엔진이 피연산자의 포인터를 먼저 resolve한 뒤 연산자를 적용합니다. - 단일 키 객체의 키가 등록된 연산자면 연산으로, 아니면 일반 객체로 취급합니다.
연산자 표
| 분류 | 연산자 | 의미와 예 |
|---|---|---|
| 조건 | if (별칭 ?:) | { "if": [조건, 참값, 조건2, 참값2, …, 기본값] }. 첫 참 조건의 값, 없으면 마지막 기본값. |
| 논리 | and, or | 단축 평가. and는 첫 falsy(또는 마지막), or는 첫 truthy(또는 마지막)를 값으로 돌려줍니다. |
| 논리 | ! (not), !! (to-bool) | { "!": x }는 truthy 부정, { "!!": x }는 truthy 여부. 존재 검사에 !!를 자주 씁니다. |
| 동등 | ==, != | 느슨한 비교(숫자 강제 변환 후 비교. "1"==1은 참). |
| 동등 | ===, !== | 엄격한 비교(타입까지). |
| 비교 | <, <=, >, >= | 연쇄 가능: { "<": [1,2,3] }은 1<2 AND 2<3. 숫자화 불가(NaN)면 false. |
| 산술 | + | 모든 피연산자의 합. |
| 산술 | - | 피연산자 1개면 음수화, 2개면 뺄셈. |
| 산술 | *, /, % | 곱, 나눗셈, 나머지. |
| 집계 | min, max | 피연산자들의 최소, 최대. |
| 문자열 | cat | 모든 피연산자를 문자열로 이어 붙임. |
| 포함 | in | { "in": [needle, haystack] }. haystack이 문자열이면 부분문자열, 컬렉션이면 원소 포함. |
| 배열 | merge | 여러 배열이나 값을 하나의 배열로 평탄화(누적 수집에 씀). |
배열 순회 연산자(map, filter, reduce, all, some, none)는 지원하지 않습니다. Script는 배열을 Loop로 순회합니다(Statement 카탈로그의 Loop).
숫자 변환과 예시
숫자 변환 규칙은 다음과 같습니다. 숫자는 그대로 두고, true는 1, false는 0, 문자열은 파싱하며(파싱 불가면 계산 실패값), null은 0으로 변환합니다.
{ "-": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] } // 잔액 - 비용
{ "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] } // 잔액 < 비용 → boolean
{ "and": [ { "<": [ "{ /a/body/risk }", 0.5 ] }, { ">=": [ "{ /b/body/score }", 700 ] } ] }
{ "cat": [ "id-", "{ /payload/sys/id }" ] } // "id-<uuid>"
{ "!!": "{ /found/sys/id }" } // 존재하면 true
{ "merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] } // 배열에 원소 하나 누적
{ "if": [ "{ /page/next }", "{ /page/next }", "END" ] } // next가 있으면 next, 없으면 "END"참과 거짓 판정 (Truthiness)
if, and, or, !, !!와 If.condition, Loop.while은 아래 규칙으로 참과 거짓을 가립니다.
- falsy:
null,false, 숫자0, 빈 문자열"", 빈 컬렉션(빈 배열). - truthy: 그 외 전부(0이 아닌 숫자, 비어 있지 않은 문자열과 배열, 모든 객체).
키도 참조할 수 있습니다
fields 같은 맵의 키도 { /ptr } 참조를 지원합니다. 키가 런타임에 resolve됩니다.
"fields": { "{ /payload/fields/fieldName }": { "en-US": "{ /payload/fields/fieldValue }" } }두 키가 같은 값으로 resolve되면 충돌이 나고 엔진 에러입니다.
로케일 맵 (LocaleValueMap): Content와 Media 특유 규칙
WEEGLOO Content와 Media의 각 필드는 값이 아니라 로케일별 맵입니다(예: balance가 { "en-US": 1, "ko-KR": 10 }). 따라서 읽고 쓸 때 로케일을 함께 다뤄야 합니다. Media도 title과 description(스칼라), file(인제스트 지시)이 로케일 맵입니다. /payload나 HTTP 응답처럼 Content나 Media가 아닌 JSON은 이 규칙과 무관합니다(스키마가 정한 구조 그대로이고, 스칼라면 스칼라입니다).
읽기
- 스칼라를 얻으려면 로케일까지 지정합니다:
{ /<name>/fields/<field>/<locale> }(예:{ /post/fields/title/en-US }). - 로케일 없이
{ /<name>/fields/<field> }면 로케일 맵 객체 전체가 나옵니다. localized:false필드는 기본 로케일 버킷에만 있으므로 그 기본 로케일 코드로 읽습니다.
쓰기 (ResourceCreate, ResourceUpdate, ResourcePatch의 fields)
값은 로케일 맵 { "<locale>": <스칼라 값표현식> }입니다. 읽기와 대칭입니다.
"fields": {
"title": { "en-US": "Hello", "ko-KR": "안녕" }, // 여러 로케일은 버킷 나열
"status": { "en-US": "paid" }
}ResourceCreate는 populate하는 모든 필드에 space 기본 로케일 버킷을 반드시 포함해야 합니다(default-locale 규칙).ResourceUpdate는 전체 교체입니다.fields에 없는 필드와 로케일은 제거됩니다(file 포함).ResourcePatch는 지정한 필드와 버킷만 갱신합니다(나머지 필드와 로케일은 유지).- 리터럴
null로 삭제합니다: 값이 리터럴null이면 그 (field, locale) 버킷을 삭제합니다(Patch에서 특정 로케일 비우기의 표준).""(빈 문자열)은 삭제가 아니라 빈 값 설정입니다. 값표현식({ /ptr })이 런타임에 null로 평가되면 삭제가 아니라 에러입니다(payload 누락을 조용히 삼키지 않음). 삭제는 리터럴null만 해당합니다. Mediafile: 값이 스칼라가 아니라 인제스트 지시{ "source": …, "encoding": "url"|"base64" }입니다. 파일이 포함된 쓰기는 Async 전용입니다(Statement 카탈로그의 ResourceCreate).localized:false필드는 기본 로케일 버킷에만 넣습니다.- 로케일 코드(맵 키)도
{ /ptr }참조가 가능합니다(위 키도 참조할 수 있습니다 참조). 동적 로케일을 만들 때 씁니다.
locale 편의 필드
ResourceCreate, ResourceUpdate, ResourcePatch에 locale을 주면 엔진이 fields의 각 값을 자동으로 { <locale>: 값 } 버킷으로 감쌉니다. 즉 스칼라만 주면 됩니다.
// 아래 두 개는 동일하다
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
"locale": "en-US", "fields": { "title": "Hello" } }
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
"fields": { "title": { "en-US": "Hello" } } }locale을 주면서 값에 이미 로케일 맵({ "en-US": … })을 중첩하면 { <locale>: { "en-US": … } }로 이중 중첩됩니다(작성자 실수). locale을 쓰면 스칼라만, 안 쓰면 명시 로케일 맵만 쓰도록 하나로 통일합니다.
where와 order의 로케일
where와order에서fields.X는 엔진이 space 기본 로케일을 자동 적용합니다(CMA 조회와 동일).- 특정 로케일을 노리려면
fields.X.<locale>로 명시합니다.
"where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } } // 기본 로케일 slug
"where": { "fields.title.ko-KR": { "prefix": "안" } } // 특정 로케일관련 문서
- Statement 카탈로그: 값 표현식을 쓰는 17종 문의 필드와 결과.
- 실행 시맨틱, 제약, 보안: 실행 순서, 에러, 낙관적 잠금, 정적 제약.
- 쿡북: 값 표현식을 조합한 완결 예시.
- Script 개요: 최상위 구조와 실행 모드.
