실행 시맨틱, 제약, 보안

Script가 런타임에 어떻게 동작하는지(순서, 트랜잭션, 에러, 잠금), 저장 시 어떤 정적 제약을 받는지, 그리고 보안 모델을 정리합니다. 문법은 Statement 카탈로그와 값 표현식, 실전 조합은 쿡북을 참조하세요.

실행 순서

  • statements는 위에서 아래로 순차 실행됩니다. Return에 도달하면 그 지점에서 종료합니다.
  • 실행은 호출 요청을 처리하는 경로에서 인라인으로 이루어집니다. 호출의 응답이 곧 실행 결과이고(응답 형태는 Script 개요의 요청과 응답 참조), 한 번의 실행에 주어지는 시간은 아래 시간 예산에서 다룹니다.

실행 시맨틱

Guard (사전 조건)

전용 guard Statement는 없습니다. If와 then:[Return]으로 표현합니다. 조건 위반 시 결과를 반환하고 이후 Statement는 실행하지 않습니다(guard가 없는 Script도 당연히 가능합니다).

{ "type": "If", "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
  "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" }, "statusCode": 402 } ] }

트랜잭션 없음과 best-effort 보상

Script는 트랜잭션이 아닙니다. 실패 시 엔진이 지금까지 한 작업의 보상(compensation)을 시도하고 에러 원인을 리턴하지만, 다음 한계가 있습니다(설계상 감수한 것입니다).

  • 삭제를 되돌리면 새 sys.id가 만들어지므로, 이를 가리키던 참조가 깨집니다.
  • 외부 효과(Http)는 비가역입니다(이미 나간 호출과 과금은 되돌릴 수 없습니다).
  • 보상이 아예 실행되지 않아 미보상 상태가 남을 수 있습니다.

진짜 원자성이 필요하면 사용자가 Script로 직접 보상하거나, 되돌릴 수 없는 작업(외부 호출 등)을 맨 마지막에 배치합니다. "연쇄는 되는데 롤백은 안 되면서 안전해 보이는" 순서가 가장 위험합니다.

낙관적 잠금

update/patch 경합은 ResourceUpdate와 ResourcePatch의 version으로 좁힙니다. version(값표현식, Int)을 주면 대상의 현재 sys.version과 일치할 때만 갱신하고, 불일치는 버전 충돌 에러로 abort됩니다(Try/catch로 국소 처리 가능). 생략하면 검사 없이 last-write-wins입니다. 보통 ResourceRead나 ResourceFind로 먼저 읽어 그 sys.version을 넘깁니다(쿡북의 낙관적 잠금 CAS 참조).

origin 기준 쓰기

쓰기는 항상 origin(draft)에 반영되고, delivery(CDA/ACDA) 노출은 publish로 제어합니다(ResourceCreate/ResourceUpdate/ResourcePatch의 publish, 또는 ResourcePublish/ResourceUnpublish).

무엇이 실패인가

실행 도중 Statement 하나가 실패하면, 그 실패를 처리하는 Try가 없는 한 실행은 그 자리에서 중단됩니다. 이때 엔진은 그때까지 수행한 작업을 되돌리려고 시도합니다. 되돌리기에는 한계가 있으며, 자세한 내용은 트랜잭션 없음과 best-effort 보상에서 다룹니다.

실패한 Statement가 Try 안에 있으면 그 실패를 catch에서 처리할 수 있습니다. catch 안에서는 /error로 실패 내용({ message })을 참조합니다. 어느 Statement에서 실패했는지는 담기지 않습니다.

다음 경우가 실패로 처리됩니다.

Return은 실패가 아닙니다. 실행을 정상적으로 일찍 끝내는 것이므로 catch로 넘어가지 않습니다.

Http가 실패하는 두 방식

Http의 실패는 status 응답으로 처리되는지에 따라 두 가지로 나뉩니다. 이 차이에 따라 retry와 ignoreStatusCode가 적용되는지가 달라집니다.

구분status 응답으로 처리되는 경우곧바로 실패하는 경우
해당하는 경우대상 API가 400 이상의 status를 응답한 경우, 시간이 초과된 경우(status 504), 대상 API에 연결하지 못했거나 응답을 읽지 못한 경우(status 502), 응답 본문이 10MiB를 넘는 경우(status 502)responseType이 "Json"인데 성공 응답의 본문이 JSON이 아닌 경우, 보낼 파일이 다운로드 한도를 넘는 경우, 파일 URL이 2xx가 아닌 status를 응답하거나 파일 URL에 연결할 수 없는 경우, part 값의 형식이 잘못된 경우
retry지정한 횟수만큼 재시도합니다재시도하지 않습니다
ignoreStatusCode: true실패로 보지 않고 { status, body }를 결과로 바인딩합니다적용되지 않으며 실패로 처리됩니다
Try/catchcatch에서 처리할 수 있습니다catch에서 처리할 수 있습니다

파일 전송 과정에서 실패하는 조건은 파일을 본문으로 보내기에서 자세히 다룹니다.

서버측 집계는 건수만

건수는 ResourceCount가 서버에서 셉니다. 항목을 읽어 오지 않으므로 처리 항목 수 상한을 받지 않습니다.

sum과 group-by는 전용 서버 작업이 없습니다. 그런 집계는 ResourceForEach로 순회하면서 SetVar와 JsonLogic으로 직접 계산해야 하고, 따라서 처리 항목 수 상한에 묶입니다(수백만 건 집계엔 부적합). 건수만 필요하면 순회하지 말고 ResourceCount를 씁니다.

대기와 지연 없음

Script에는 Delay Statement가 없습니다. Script는 한 번 실행되고 끝이며, 외부 job이 끝날 때까지 내부에서 대기하거나 폴링하지 않습니다.

정적 제약 (저장 시 검증)

아래는 Script를 저장(생성/수정)하는 시점에 검사됩니다. 위반하면 저장이 거부됩니다(런타임이 아니라 저작 시 실패). 어떤 위반이 어떤 코드로 거부되는지는 오류에서 다룹니다.

제약값
한 정의당 외부 호출 최대(Http·EmailSend)플랜별(요금제 참조)
ResourceForEach 총 처리 항목 최대(선언한 limit이 없으면 이 값까지 순회, 매치가 남은 채 걸리면 실패)10,000
한 정의당 SetVar 최대(중첩 포함)10
한 정의당 Cache 최대(중첩 포함, 연산과 무관하게 합산)5. 넘으면 저장 거부
Loop·ResourceForEach 블록 안의 Cache저장 거부
Cache.key리터럴만, 최대 128자. 값 표현식이면 저장 거부
Cache.ttl1에서 30초 사이, 생략 시 5초. 벗어나면 저장 거부
한 정의당 전체 Statement 최대(중첩 포함)플랜별(요금제 참조)
Http.retry 상한2
한 Http의 요청 본문 필드(body·urlBody·multipartBody)셋 중 하나만. 둘 이상 적으면 저장 거부
Http.multipartBody의 part 수1개 이상. 비어 있으면 저장 거부
multipartBody part의 value·url정확히 하나. 둘 다 적거나 둘 다 비우면 저장 거부
multipartBody part의 fileNameurl을 적은 part에만. url 없이 적으면 저장 거부
Regex.pattern 길이128자
한 정의당 secrets 최대20
secrets의 key1~64자, 영문·숫자·_·-만. 같은 key가 둘이면 저장 거부
secrets의 value1~4,096자. 벗어나면 저장 거부
{ /secrets… } 참조정확히 /secrets/<선언한 key>. 아니면 저장 거부
ServiceUser를 만들거나 지우는 Statement저장 거부. 읽기 세 Statement와 ResourceUpdate·ResourcePatch만 이 리소스를 받습니다
anonymousCallEnabled가 true면 where의 createdBy: ":self"저장 거부
Media 인제스트 지시의 transformations(파일 변환)배열만. 원소의 type은 PdfWatermark, color는 #RRGGBB. text는 작성한 문자열 전체({ /pointer }의 경로 포함)가 쓸 수 있는 글자로 된 한 줄. 어기면 저장 거부
PdfWatermark의 리터럴 text 길이1~64자. 벗어나면 저장 거부. { /pointer }가 든 text는 저장할 때 검사하지 않고, 실행할 때 해석된 값으로 검사

위 표의 고정 한도는 플랫폼이 정한 값이라 플랜과 무관하게 같습니다. 반면 한 정의당 전체 Statement 수와 외부 호출 수는 플랜별 한도입니다. 이 둘은 유효성 오류가 아니라 플랜 한도라서, 초과하면 저장·수정이 플랜 한도 초과로 거부되고(같은 정의도 상위 플랜에서는 허용됩니다) 업그레이드로 풀립니다. 플랜별 수치는 요금제에 있습니다.

Media 파일 인제스트는 Http·EmailSend 같은 외부 호출과 달리 한 정의당 외부 호출 한도에 포함되지 않습니다.

ResourceForEach는 자식을 소유하는 복합 Statement라 그 자체는 외부 호출 수에 세지 않습니다. onEach 안의 외부 호출 Statement(Http·EmailSend)가 세어집니다(정적으로는 1로 세지만 순회하며 항목마다 실제로 실행됩니다). onEach에는 외부 호출이나 Media 파일 인제스트를 담을 수 있으며, 이는 Loop의 body도 마찬가지입니다. 반복이 실제로 몇 번 도는지는 이 카운트에 들어가지 않고, 대신 아래 시간 예산에서 곱셈으로 들어갑니다.

값 길이 상한 (런타임)

서명과 텍스트 처리 Statement, 그리고 Cache는 다루는 값의 크기에 상한이 있습니다. 표현식의 길이가 아니라 그 표현식이 resolve된 값의 길이이고({ /rawPayload } 열여섯 자가 수십 KB를 가리킵니다), 그래서 저장 시점이 아니라 실행 중에 검사됩니다.

대상상한초과하면
Signature의 value65,536자그 Statement가 실패(status 422)
Hash의 value128자그 Statement가 실패(status 422)
Regex의 value10,240자(10KiB)그 Statement가 실패(status 400)
Cache의 value10,240바이트(10KiB)그 Statement가 실패(status 422)
  • 넷 다 다른 런타임 실패와 같아서 Try/catch로 국소 처리할 수 있습니다.
  • Signature의 상한은 실제 제공자가 보내는 본문 크기에 맞춘 것입니다(결제 이벤트는 수 KB, 주문 웹훅은 수십 KB에 이릅니다). Hash는 필드 몇 개를 이어 붙이는 자리라 훨씬 좁습니다.
  • Regex.pattern의 128자는 위 정적 제약에 있는 저장 시 검사입니다. 이 길이는 폭주를 막는 장치가 아닙니다((a+)+$는 여섯 자로도 위험합니다). 폭주를 막는 것은 패턴을 리터럴로만 쓰게 한 규칙과 아래 시간 예산이고, 이 길이가 보장하는 것은 사람이 읽고 검토할 수 있는 크기뿐입니다.

시간 예산 (런타임)

실행 한 번에 허용되는 시간은 최대 180초입니다. 실제로 허용되는 시간은 Script 정의(파일을 보내는 경우에는 플랜도)에 따라 정해지며, 기본 30초에 외부 호출이 사용할 수 있는 시간을 더한 값입니다.

이 예산은 실행 전체에 적용됩니다. 이와 별도로 Http 요청 한 번은 그 요청의 timeoutMs 안에 끝나야 합니다.

기본 30초

리소스를 읽고 쓰는 작업이나 Media 파일을 받아 처리하는 작업처럼 외부 호출이 아닌 작업은 모두 이 30초 안에서 처리됩니다. 외부 호출이 없는 Script에는 30초가 허용됩니다.

Statement별로 더해지는 시간

순서대로 놓인 Statement의 시간은 모두 더합니다.

Statement더해지는 시간
HttptimeoutMs(지정하지 않으면 30초). 파일을 보내면 파일마다 다운로드 시간(아래 표)을 더하고, retry를 지정했으면 전체에 시도 횟수(1 + retry)를 곱합니다
EmailSendtimeoutMs(지정하지 않으면 10초). 재시도하지 않으므로 한 번만 더합니다
Ifthen과 else 가운데 더 긴 쪽의 시간
Parallelbranches 가운데 가장 긴 것의 시간
Trybody, catch, finally의 시간을 모두 더한 값
Loopbody의 시간에 maxIterations(지정하지 않으면 10,000)를 곱한 값
ResourceForEachonEach의 시간에 limit(지정하지 않으면 10,000)를 곱한 값
그 밖의 Statement(다른 Statement를 담지 않는 것)더하지 않습니다. 기본 30초 안에서 처리됩니다

반복 안에 외부 호출이 없으면 반복 횟수를 곱해도 더해지는 시간은 0입니다. 따라서 그런 반복은 기본 30초 안에 끝나야 합니다.

파일 하나당 다운로드 시간

Plan다운로드 시간
Free약 5.2초
Basic약 10.5초
Pro약 10.5초
Enterprise약 107.4초

이 값은 플랜의 다운로드 한도만큼을 초당 10,000,000바이트로 받는 데 걸리는 시간입니다.

계산 예

Pro 플랜에서 사진 두 장을 보내는 Http(timeoutMs 30초, retry 1)를 하나 호출하는 Script는 다음과 같이 계산합니다.

  • Http: (30초 + 10.5초 × 2) × 2 = 약 102초
  • 실행 전체: 30초 + 102초 = 약 132초

132초는 180초보다 짧으므로 이 Script에는 132초가 허용됩니다.

180초를 넘는 경우

계산한 값이 180초를 넘어도 Script는 저장되고 실행됩니다. 다만 실행이 180초에 도달하면 그 시점에서 중단됩니다.

플랜별 개수 한도

Script는 Organization당 개수가 플랜별로 제한됩니다.

PlanScript 개수
Free10
Basic30
Pro100
Enterprise무제한

이와 별개로, 한 Script 정의가 담을 수 있는 Statement 수와 외부 호출(Http·EmailSend) 수도 플랜별로 제한됩니다. 정의를 저장·수정할 때 그 플랜의 한도를 넘으면 거부되며, 구체 수치는 요금제를 참조하세요.

한도에 도달하면 새 Script 생성이 거부됩니다.

Http 파일 다운로드 크기

Http가 요청 본문으로 보낼 파일을 URL에서 받아 올 때(urlBody, multipartBody의 url part), 파일 URL 하나당 받을 수 있는 크기가 플랜별로 제한됩니다. 그 Script를 소유한 Space가 속한 Organization의 플랜을 따릅니다.

Plan파일 URL 하나당 최대 크기
Free50MiB
Basic100MiB
Pro100MiB
Enterprise1GiB
  • 한도는 URL마다 따로 적용됩니다. url part가 여럿이면 각 파일이 이 한도 안에 있어야 합니다.
  • 한도를 넘는 파일을 보내려고 하면 그 Http가 실패합니다(Http가 실패하는 두 방식 참조).
  • 이 한도는 시간 예산에 더해지는 다운로드 시간의 기준이기도 합니다.

보안 모델

secrets

외부 API 키, 결제 대행사(PG)의 시크릿 키, 웹훅 서명 키, 미리 계산한 Basic … 인증값 같은 비밀 값은 Statement에 값을 그대로 적지 않습니다. 정의의 secrets에 선언하고, Statement에서는 { /secrets/<key> }로 읽습니다. 공개된 테스트 키도 같은 자리에 둡니다. 그러면 실제 키로 바꿀 때 Statement는 그대로 두고 secrets의 값만 바꾸면 됩니다.

"secrets": [
  { "key": "LLM_API_KEY",            "value": "sk-...",    "description": "LLM 호출용 API 키" },
  { "key": "WEBHOOK_SIGNING_SECRET", "value": "whsec_...", "description": "" }
],
"statements": [
  { "type": "Http", "method": "POST", "url": "https://api.openai.com/v1/chat/completions",
    "headers": [ { "key": "Authorization", "value": "Bearer { /secrets/LLM_API_KEY }" } ],
    "body": { /* ... */ }, "name": "llm" }
]
필드필수규칙
key필수1~64자이고 영문·숫자·_·-만 씁니다. 한 Script 안에서 유일해야 합니다.
value필수1~4,096자 문자열입니다. 정적 값이라 템플릿으로 해석하지 않으므로, 안에 { /… }를 넣어도 글자 그대로 쓰입니다.
description필수용도 설명이며 0~128자입니다. 빈 문자열 ""도 됩니다.
  • 저장: value는 WEEGLOO가 암호화해 안전하게 저장합니다. Statement는 { /secrets/<key> }로 원래 값을 읽습니다.
  • 조회: Script를 돌려주는 API 응답(조회·생성·수정)에는 value가 원래 값으로 반환됩니다. 그 Script를 읽을 수 있는 역할에게는 값이 보이므로 읽기 권한을 좁게 둡니다. 회원(ServiceUser)은 Script 정의를 읽을 수 없습니다(조회와 저작은 CMA 전용이고 ACMA에는 Script API가 없습니다).
  • 수정: 전체 수정(PUT)은 정의 전체를 교체합니다. secrets를 빼고 보내면 선언해 둔 비밀 값이 지워지고, Statement가 아직 그 값을 { /secrets/… }로 참조하고 있으면 저장이 거부됩니다. 그래서 수정할 때마다 secrets를 함께 보냅니다. 조회 응답의 값을 그대로 돌려보내면 됩니다.
  • 실행 중의 값: 읽은 값으로 무엇을 할지는 Statement가 정합니다. 읽은 값을 Return으로 돌려주면 실행 응답과 ScriptLog에 남고, SetVar·Cache·Content에 넣으면 그 자리에 원래 값으로 남습니다. 비밀 값은 그 값이 필요한 외부 API에만 보냅니다.
  • App Bundle: Script가 App Bundle로 패킹될 때 secrets의 value는 ******로 가려지고 key와 description은 그대로 실립니다. 그 App Bundle을 설치해 만들어진 Script는 비밀 값이 ******인 상태이므로, 설치한 쪽에서 실제 값을 넣어야 그 값을 읽는 Statement가 동작합니다. secrets 밖의 자리(Http의 헤더, Signature의 secret 등)에 적은 값은 가려지지 않고 그대로 실립니다.

한 Script에는 secrets를 20개까지 둘 수 있습니다. 포인터 규칙은 값 표현식의 /secrets에서 다룹니다.

실행 신원과 인가

  • 실행 신원: 실행 중 모든 리소스 작업은 /execute를 호출한 사용자 신원으로 수행됩니다. 만들어지거나 수정되는 리소스의 createdBy/updatedBy가 호출자이고, createdBy: ":self" 스코프도 호출자를 기준으로 풀립니다. 예외는 익명 호출입니다. /execute/anonymous로 들어온 실행에는 호출자가 없으므로 두 가지 모두 작성자를 기준으로 풀립니다(익명 호출).
  • 인가 경계는 둘이며, 런타임에는 Statement마다 리소스 권한을 재검사하지 않습니다.
    1. 저작 시(저장): Script를 저장할 때, 작성자가 그 Statement들이 쓰는 리소스와 액션 권한을 실제로 갖는지 검사합니다. 하나라도 없으면 저장이 거부됩니다. 즉, 권한 없는 작업이 담긴 Script는 애초에 저장되지 않습니다. 리소스를 고르는 Statement는 leaf든 블록을 소유하는 ResourceForEach든 모두 이 검사를 받습니다. 이미 저장해 둔 정의도 수정할 때 다시 검사되므로, 권한이 회수된 뒤에는 그 정의를 고쳐 저장할 수 없습니다.
      • 회원 디렉터리(ServiceUser)는 권한 맵이 아니라 설정 축으로 검사합니다. 읽기 세 Statement와 ResourceUpdate·ResourcePatch에 resource: "ServiceUser"를 쓰려면 작성자의 SpaceRole settings에 SETTING_SERVICE_LOGIN(또는 SETTING_ALL)이 있어야 합니다(SpaceRole의 settings). 회원 디렉터리가 다른 모든 경로에서도 Space 설정이 관장하는 리소스이기 때문입니다.
      • 회원을 만들거나 지우는 Statement는 어떤 역할로도 저장되지 않습니다. Script는 회원을 만들거나 지울 수 없으므로, 권한 부족(403)이 아니라 잘못 쓴 Statement(400)로 거부됩니다. 권한을 더해 닫을 수 있는 공백이 아니라는 뜻입니다. 허용되는 변경은 ResourceUpdate·ResourcePatch의 roleOverride와 enableLogin뿐입니다(회원 디렉터리 다루기).
    2. 호출 시(/execute): 호출자의 Script Execute 권한만 검사합니다. 권한이 없으면 403입니다. 통과하면 Statement별 리소스 권한은 런타임에 다시 확인하지 않고 실행합니다. 프로그래밍의 함수 실행 권한과 같은 방식입니다. 함수를 실행할 권한만 있으면 그 안의 개별 작업 권한은 다시 묻지 않습니다. 익명 호출 경로에는 이 검사가 없습니다. 검사할 호출자가 없기 때문이고, 그래서 그 경로를 여는 것은 Script 하나를 무인증으로 공개하는 일과 같습니다.
  • 직접 호출 차단 (directCallEnabled): Script의 directCallEnabled가 false이면 /execute 직접 호출 자체가 거부됩니다. 이 게이트는 Execute 권한 검사를 통과한 뒤에 걸리므로, Execute 권한이 있어도 막힙니다. 권한이 없는 호출자는 이 게이트에 닿기 전에 403을 받습니다. 이 게이트는 그 엔드포인트에만 있으므로, Webhook의 연결 액션(script)과 Scheduler는 그대로 실행합니다. 기본값은 true(직접 호출 허용)입니다.
  • 익명 호출 (anonymousCallEnabled): 기본값은 false입니다. true로 두면 그 Script만 인증 없는 전용 경로(/execute/anonymous)로도 실행되고, 그때 실행 신원은 호출자가 아니라 작성자입니다. 위 두 경계 중 호출 시 검사(Execute 권한)가 그 경로에는 없으므로, 실질적인 인증은 Script가 스스로 합니다(받은 요청의 서명 검증). 켜는 조건과 저장 규칙은 익명 호출에서 다룹니다.
  • 소유권 스코프: where 필터의 createdBy: ":self"는 "현재 호출자가 만든 것만"을 뜻합니다(예: 내 지갑만 조회). 익명 호출을 허용한 Script에서는 이 필터를 쓸 수 없습니다. 호출자가 없어 작성자로 풀리므로, 소유권 스코프라는 원래 뜻이 성립하지 않습니다.
  • 위임된 권한 (작성 주의): 위 두 경계를 합치면, Script 실행은 작성자의 권한을 위임받아 수행되는 것과 같습니다. 호출자는 Execute 하나만 있으면 되고, Script 안 Statement는 작성자가 저장 시 인가받은 범위에서 그대로 실행됩니다. 따라서 호출자가 스스로는 하지 못하는 리소스 작업도 Script를 통해 일어날 수 있습니다. 작성자에게 부여된 권한이 곧 그 Script의 영향 범위이므로, Script에 담는 동작은 신중히 정합니다.

요약 체크리스트

저장 전에 다음을 확인합니다.

  • 익명 호출(anonymousCallEnabled)을 켰다면 where에 createdBy: ":self"가 없고, 받은 요청을 검증하는 Statement(Signature 등)를 맨 앞에 두었습니다.
  • 외부 호출(Http·EmailSend) 수와 전체 Statement 수는 플랜 한도 이내이고, SetVar는 10개 이하, Cache는 5개 이하입니다.
  • Cache를 썼다면 key를 리터럴로 적었고 Loop나 ResourceForEach 안에 두지 않았습니다.
  • ResourceForEach로 큰 집합을 돌면 limit을 선언하거나 완주 가능한 크기인지 확인했습니다.
  • 반복(Loop·ResourceForEach)을 담았다면 그 반복이 곱셈으로 시간 예산에 들어가는 것을 확인했습니다(외부 호출이 없으면 30초 기본 예산이 한도입니다).
  • 외부 API 키나 서명 키 같은 비밀 값은 secrets에 선언하고 { /secrets/<key> }로 읽었습니다. Statement에 값을 그대로 적은 비밀 값은 없습니다.
  • 전체 수정(PUT)으로 저장한다면 secrets를 함께 보냈습니다.
  • 서명 검증에 쓰는 메시지는 /payload가 아니라 { /rawPayload }로 지정했습니다.
  • 회원(ServiceUser)을 다루는 Statement가 있으면 작성자에게 SETTING_SERVICE_LOGIN이 있고, 그 리소스를 만들거나 지우는 Statement는 넣지 않았습니다.
  • 되돌릴 수 없는 작업(외부 호출)은 되도록 뒤에 두었습니다.
  • Http로 파일을 보낸다면 요청 본문 필드(body·urlBody·multipartBody)를 하나만 적었고, 각 파일이 플랜별 다운로드 한도 안인지 확인했습니다.
  • update/patch 경합이 걱정되면 ResourceUpdate나 ResourcePatch의 version을 씁니다.
  • 결과를 돌려주려면 Return.value를 명시했습니다.

오류

정의의 형태가 정적 제약을 어겨 저장이 거부될 때 나오는 코드입니다. 값 표현식 규칙을 어긴 코드는 값 표현식의 오류에, 호출·삭제 시 나오는 코드는 엔드포인트의 오류에 있습니다. 모든 리소스에 공통인 코드는 공통 오류를 참조하세요.

코드조건
WGL400066한 정의에 담은 Cache Statement가 5개를 넘습니다.
WGL400068Cache Statement를 Loop나 ResourceForEach의 블록 안에 두었습니다.
WGL400067Cache Statement의 key에 리터럴이 아니라 { /pointer } 참조를 적었습니다.
WGL400065Cache Statement의 ttl이 허용 범위를 벗어났습니다.
WGL400063Cache Statement에서 action에 해당하지 않는 필드를 적었습니다(Get에 ttl, Set에 defaultValue).
WGL400060ResourceCreate·ResourceDelete와 발행·보관 Statement의 resource에 "ServiceUser"를 적었습니다. 회원 디렉터리는 조회 세 Statement와 ResourceUpdate·ResourcePatch만 받습니다.
WGL400077ResourceCount의 resource에 셀 수 없는 리소스를 적었습니다. Media와 회원 디렉터리(ServiceUser)는 세지 못합니다.
WGL400037EmailSend에 to와 toServiceUser를 함께 적었거나 둘 다 비웠습니다. 정확히 하나만 적습니다.
WGL400054EmailSend의 수신자 합계(to 1명 + cc + bcc)가 50명을 넘습니다.
WGL400039EmailSend의 timeoutMs가 상한을 넘습니다.
WGL400101EmailSend에 body와 templateBody를 함께 적었거나 둘 다 비웠습니다. 정확히 하나만 적습니다.
WGL400102EmailSend의 templateBody.source가 이 Script의 ResourceRead·ResourceFind가 읽은 Content를 가리키는 포인터 하나가 아닙니다. 포인터 앞뒤에 다른 글자가 있어도 거부됩니다.
WGL400061익명 호출을 허용한 Script(anonymousCallEnabled)에서 읽기 Statement의 where에 createdBy: ":self"를 적었습니다.
WGL400023한 정의에 담은 SetVar Statement가 10개를 넘습니다.
WGL400026Http Statement의 retry가 상한 2를 넘습니다.
WGL400087Http에 요청 본문 필드(body·urlBody·multipartBody)를 둘 이상 적었습니다.
WGL400088Http의 multipartBody에 part가 하나도 없습니다.
WGL400089multipartBody의 part에 value와 url을 둘 다 적었거나 둘 다 적지 않았습니다.
WGL400090multipartBody의 part에 url 없이 fileName을 적었습니다.
WGL400036ResourceForEach가 처리할 수 있는 항목 수 상한을 넘습니다.
WGL400094secrets에 같은 key를 쓰는 항목이 둘 이상입니다.
WGL400095한 정의에 담은 secrets가 20개를 넘습니다.
WGL400086secrets의 value가 1~4,096자를 벗어납니다. 오류에는 value가 실리지 않습니다.
WGL400086{ /pointer }가 없는 PdfWatermark의 text가 1~64자를 벗어납니다.
WGL400096Media 인제스트 지시의 transformations가 배열이 아니거나, 원소의 type이 PdfWatermark가 아니거나, PdfWatermark에 text가 없거나 객체·배열이거나, color가 #RRGGBB 형식이 아니거나, 작성한 그대로의 text에 쓸 수 없는 글자나 줄바꿈이 들어 있습니다.
WGL429005한 정의에 담은 전체 Statement 수가 요금제 한도를 넘습니다.
WGL429006한 정의에 담은 외부 호출(Http·EmailSend) 수가 요금제 한도를 넘습니다.
WGL403015정의에 담은 Statement가 다루는 리소스와 액션의 권한을 작성자가 갖고 있지 않습니다. 권한이 있어도 그 허용에 contentType·createdBy·tag 필터가 붙어 있으면 저장이 거부됩니다. 조건 없는 허용이라야 합니다. 유일한 예외는 Content Create로, 이때는 contentType 범위를 정한 허용도 인정되며 Statement에 적은 contentType과 대조합니다(Media Create에는 이 예외가 없습니다). 읽기 Statement의 resource에 "ServiceUser"를 적었는데 작성자에게 SETTING_SERVICE_LOGIN이 없는 경우도 이 코드입니다.