ServiceUserRole
ServiceUserRole은 제품에 가입한 end-user, 곧 ServiceUser에게 주는 권한 묶음입니다. Content Type·Content·Media에 대해 무엇을(읽기·생성·편집·삭제) 할 수 있는지, Script를 실행할 수 있는지, 그리고 어떤 Content Type만, 자기가 만든 것만 등으로 범위를 좁히는 필터를 한 리소스에 담습니다. 쓸 수 있는 액션은 맵마다 다릅니다(아래 권한 맵 표). 이 권한은 ServiceUser가 호출하는 ACMA/ACDA에 적용됩니다.
ServiceUserRole은 SpaceRole과 쓰이는 자리가 다릅니다. SpaceRole은 Weegloo User(콘텐츠 스튜디오 사용자)의 권한 묶음으로 CMA/CDA에 적용되고, ServiceUserRole은 제품에 가입한 ServiceUser의 권한 묶음으로 ACMA/ACDA에 적용됩니다. 만든 ServiceUserRole은 그 자체로는 아무에게도 적용되지 않습니다. ServiceLogin의 기본 역할(defaultRole)에 지정하거나 ServiceUser의 roleOverride에 바인딩해 부여합니다.
리소스 구조
다음은 ServiceUserRole "구매자"의 단일 조회 응답입니다. sys(시스템 속성)와 함께, 권한을 정하는 본문 속성 contentType·content·media·script를 가집니다.
{
"sys": {
"id": "3trmXRLXeZN2RTHvVj3hFDN5546vbp",
"type": "ServiceUserRole",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
"createdAt": "2026-06-18T12:40:36.944Z",
"updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
"updatedAt": "2026-06-18T12:40:36.944Z",
"version": 1
},
"name": "구매자",
"description": "발행된 상품을 읽을 수 있는 회원",
"contentType": { "All": { "Allow": [] } },
"content": {
"Read": {
"Allow": [
{ "contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } } }
]
}
},
"media": { "All": { "Allow": [] } },
"script": {}
}주요 키:
contentType: Content Type 자체(스키마)에 대한 권한 맵입니다. ServiceUser는 양식을 읽기만 하므로Read만 정합니다.content: Content(콘텐츠 데이터)에 대한 권한 맵입니다. 위 예시는 특정 Content Type의 Content만 읽도록 한정한 모습입니다.media: Media(파일·이미지)에 대한 권한 맵입니다.script: Script(선언형 백엔드 엔드포인트)에 대한 권한 맵입니다. ServiceUser는 Script를 실행(Execute)만 합니다.
SpaceRole과 달리 ServiceUserRole에는 Space 설정 접근을 담는 settings가 없습니다. ServiceUser는 Space 설정을 다루지 않기 때문입니다. 또 기본 제공 여부를 나타내는 sys.isLocked도 없습니다.
시스템 속성 (sys)
모든 ServiceUserRole은 공통 시스템 속성을 sys 객체에 담습니다. space·createdBy·updatedBy는 Refer 모양({ "sys": { "id", "type": "Refer", "targetType" } })으로 들어갑니다.
| 속성 | 타입 | 설명 |
|---|---|---|
id | string | 리소스 고유 식별자. |
type | string | 리소스 종류. ServiceUserRole은 항상 "ServiceUserRole". |
space | Refer<Space> | 이 ServiceUserRole이 속한 Space. |
createdBy | Refer<User> | 생성한 사용자. |
createdAt | string (date-time) | 생성 시각. |
updatedBy | Refer<User> | 마지막으로 수정한 사용자. |
updatedAt | string (date-time) | 마지막 수정 시각. |
version | integer (≥1) | 리소스 버전. 수정할 때마다 1씩 올라갑니다. |
ServiceUserRole은 발행 개념이 없는 설정 리소스입니다. 그래서 Content·Media와 달리 sys에 publish·archive·status가 없고, version만 가집니다. version은 ServiceUserRole을 수정할 때마다 오릅니다. SpaceRole과 달리 sys.isLocked도 없습니다.
권한 맵: contentType·content·media
contentType·content·media·script는 각각 액션을 키로 갖는 맵입니다. 각 액션의 값은 Allow(허용)와 Deny(거부) 규칙 배열을 담은 객체입니다. 맵의 구조는 SpaceRole과 같지만, 쓸 수 있는 액션은 맵마다 다르고 SpaceRole보다 좁습니다.
| 권한 맵 | 쓸 수 있는 액션 |
|---|---|
contentType | All · Read |
content | All · Create · Read · Edit · Delete |
media | All · Create · Read · Delete |
script | All · Execute |
All은 그 맵에서 쓸 수 있는 액션을 한꺼번에 가리킵니다. 표에 없는 액션을 키로 넣으면 역할 저장이 거부됩니다. 발행 계열(Publish·Unpublish·Archive·Unarchive)이 어느 맵에도 없는 것은 회원이 만든 Content·Media가 만들 때 곧바로 발행되기 때문입니다(ACMA 참조).
Save라는 액션은 없습니다. 수정 권한은 Edit이고, Save는 Webhook이 구독하는 이벤트 이름입니다.
"content": {
"Read": { "Allow": [ /* 규칙 */ ], "Deny": [ /* 규칙 */ ] },
"Edit": { "Allow": [ /* 규칙 */ ] }
}각 규칙(rule) 객체는 권한 범위를 좁히는 선택적 필터를 가집니다.
self: 그 규칙이 적용되는 대상을 리소스 자신 하나로 한정합니다.contentType맵에서는 특정 Content Type 하나,script맵에서는 특정 Script 하나를 뜻합니다.contentType: 그 Content가 속한 Content Type으로 한정합니다. Content Type을 가리키는Refer를 넣습니다.createdBy: 특정 사용자가 만든 리소스만으로 한정합니다.sys.id에 특정 id를 넣으면 그 사람이 만든 것만, 예약값:self를 넣으면 "지금 호출한 ServiceUser가 만든 것만"으로 한정됩니다.tag: 특정 Tag가 달린 리소스만으로 한정합니다.
어느 필터가 어느 권한 맵에서 유효한지는 SpaceRole과 같고(contentType 맵은 self, content 맵은 contentType으로 대상을 지정합니다), 맞지 않는 필터를 넣으면 역할 저장이 거부됩니다. 맵별 표는 SpaceRole의 권한 맵에 있습니다.
createdBy필터(:self포함)를 ACDA에서 걸 때는 대상 Content Type의publishWithAuthor가true여야 합니다. ACDA는 발행 스냅샷의sys.createdBy로 이 필터를 판정하는데,publishWithAuthor가 기본값false면 스냅샷에 작성자가 없어Allow규칙은 아무것도 매칭하지 않고(회원이 자기 리소스를 빈 결과로 받습니다),Deny규칙은 아무도 걸러내지 못합니다. 반면 ACMA(관리)는 초안의sys.createdBy로 판정하므로 이 설정과 무관해, 같은 규칙이 ACMA에서는 동작해도 ACDA에서 어긋날 수 있습니다.publishWithAuthor는 회원이 글을 올리기 전에 켜야 하고 소급되지 않습니다. Content Type의publishWithAuthor설명을 참고하세요.
빈 Allow 배열 []는 그 종류 전체에 액션을 허용한다는 뜻입니다. 필터가 비어 있으니 거를 것이 없어, 모든 리소스에 그 액션이 열립니다.
SpaceRole과 달리 ServiceUserRole에는 settings가 없습니다. 권한 맵은 contentType·content·media·script 네 가지입니다. 필터 키와 :self의 의미를 비롯한 권한 맵 작성 방법은 같은 구조를 쓰는 SpaceRole의 설명도 함께 참고하세요. 다만 쓸 수 있는 액션은 위 표대로이며 SpaceRole과 다릅니다. :self를 써서 본인이 만든 Content만 편집하도록 좁히는 예시는 바로 아래 일반 회원 역할과 관리자 역할에서 보여 줍니다.
필터 키(
self·contentType·createdBy·tag)와:self의 의미는 SpaceRole의 권한 규칙 정의를 기준으로 합니다. ServiceUserRole의:self는 현재 ServiceUser가 만든 것만으로 풀립니다. 액션 목록은 SpaceRole을 따르지 않고 위 표를 따릅니다.
일반 회원 역할과 관리자 역할
회원이 다른 회원의 리소스까지 다룰 수 있는지는 규칙의 createdBy 필터로 갈립니다. 같은 액션이라도 createdBy에 :self를 넣으면 그 액션은 호출한 회원이 만든 것만 대상으로 삼고, createdBy를 넣지 않으면 다른 회원이 만든 것까지 대상이 됩니다.
일반 회원에게 주는 역할은 Edit과 Delete를 :self로 한정합니다.
"content": {
"Edit": {
"Allow": [
{
"contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } },
"createdBy": { "sys": { "id": ":self", "type": "Refer", "targetType": "User" } }
}
]
},
"Delete": {
"Allow": [
{
"contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } },
"createdBy": { "sys": { "id": ":self", "type": "Refer", "targetType": "User" } }
}
]
}
}관리자 구실을 할 회원에게는 같은 액션에서 createdBy를 뺀 역할을 따로 만들어 줍니다. 아래는 지우기만 다른 회원 것까지 열고, 고치기는 자기 것으로 남겨 둔 역할입니다.
"content": {
"Edit": {
"Allow": [
{
"contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } },
"createdBy": { "sys": { "id": ":self", "type": "Refer", "targetType": "User" } }
}
]
},
"Delete": {
"Allow": [
{ "contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } } }
]
}
}이렇게 만든 역할은 ServiceUser의 roleOverride에 지정해 그 회원 한 명에게만 적용합니다. ServiceLogin의 defaultRole을 일반 회원 역할로 두면, 새로 가입하는 회원은 관리자 역할을 받지 않습니다.
Media도 같은 방식입니다. media 맵의 액션에서 createdBy를 빼면 다른 회원이 올린 파일까지 대상이 됩니다. 다만 contentType 필터는 content 맵에서만 쓰므로 media 맵에는 넣지 않습니다.
script (Script 권한)
script는 Script에 대한 권한 맵으로, 구조는 content·media와 같습니다. 다만 ServiceUser가 Script에 할 수 있는 일은 실행뿐입니다(저작, 곧 생성·수정·삭제는 CMA의 Weegloo User 전용입니다). 그래서 ServiceUserRole의 script에 쓸 수 있는 액션은 All과 Execute 둘뿐입니다.
가입 회원이 Script를 실행할 수 있게 하려면 이렇게 씁니다.
"script": {
"Execute": { "Allow": [] }
}규칙 필터로 쓸 수 있는 것은 self(특정 Script 하나)와 createdBy 둘입니다. contentType·tag는 Script에 붙지 않는 축이라 넣으면 역할 저장이 거부됩니다. 회원에게 특정 Script 하나만 실행하도록 허용하려면 Execute의 Allow에 그 Script를 가리키는 self 규칙을 둡니다.
"script": {
"Execute": {
"Allow": [
{ "self": { "sys": { "id": "3trmXRMZcTAjDnphewjj1AaxYcaxlK", "type": "Refer", "targetType": "Script" } } }
]
}
}Script 자체의 구조와 실행 엔드포인트는 Script 리소스와 엔드포인트에서, 액션 전체 목록은 SpaceRole의 script 권한에서 다룹니다.
오류
ServiceUserRole을 다룰 때 만나는 코드입니다. 모든 리소스에 공통인 코드는 공통 오류를 참조하세요.
| 코드 | 조건 |
|---|---|
WGL400020 | 권한 맵에 그 맵에서 쓸 수 없는 필터를 넣어 ServiceUserRole 저장이 거부되었습니다. script 맵에 contentType·tag를 넣은 경우가 여기에 해당합니다. |
API
아래 모든 엔드포인트의 기준 URL은 https://cma.weegloo.com/v1이며, Authorization 헤더에 CMA를 인증하는 Bearer 토큰이 필요합니다. 역할 수정(PUT·PATCH)에는 낙관적 동시성 제어를 위해 X-Weegloo-Version 헤더(현재 리소스의 sys.version)를 함께 보내야 합니다. 생성과 삭제에는 이 헤더가 없습니다.
관련 문서
- ServiceUser: 이 역할을 받는 가입 회원(
roleOverride). - ServiceLogin: 기본 역할(
defaultRole)로 ServiceUserRole을 지정. - SpaceRole: Weegloo User용 권한 묶음(같은 권한 맵 구조).
