ACMA (App Content Management API)
최종 수정: 2026년 6월 22일
ACMA(App Content Management API)는 제품에 가입한 회원, 곧 ServiceUser가 Content와 Media를 직접 다루는 API입니다. Weegloo User가 CMA로 콘텐츠를 다루는 것과 같은 작업을 회원 신원으로 수행한다고 보면 됩니다. 회원은 ACMA로 Content와 Media를 만들고(Create) 읽고(Read) 고치고(Update) 지웁니다(Delete). 각 작업의 허용 범위는 ServiceUserRole이 정합니다.
ACMA 호출에는 ServiceLogin이 발급한 Bearer 토큰을 씁니다. 이 토큰은 ACMA와 ACDA에서만 유효하며 CMA·CDA에는 쓸 수 없습니다(토큰 발급 흐름은 Auth API 참조). 회원이 따르는 틀인 Content Type은 ACMA에서 만들거나 고치지 않습니다. Content Type은 Weegloo User가 CMA에서 관리하며, ACMA에는 Content Type 생성·수정 엔드포인트가 없습니다.
CMA와 다른 점
ACMA는 CMA에 대응하지만 신원·소유 범위·발행 동작이 다릅니다.
| 항목 | ACMA (ServiceUser) | CMA (Weegloo User) |
|---|---|---|
| 신원 | ServiceLogin에 가입한 ServiceUser. 권한은 ServiceUserRole로 정해집니다. | Weegloo User. 권한은 SpaceRole로 정해집니다. |
| 접근 범위 | 읽기·수정·삭제 범위를 ServiceUserRole이 정합니다(ACDA의 회원별 읽기와 같은 방식). 역할 규칙에서 createdBy를 :self로 걸면 그 작업이 자기가 만든 자료로 한정되며, 보통 수정·삭제에 이렇게 적용합니다. 읽기는 역할이 허용하는 만큼 다른 회원 자료까지 포함할 수 있습니다. | SpaceRole이 허용하는 범위 안에서 Space 전체 자료를 다룹니다. |
| 생성 시 발행 | Content·Media를 만들면 곧바로 발행됩니다. 별도 발행 호출이 없습니다. | Content는 만든 뒤 별도 발행 호출이 있어야 전달 경로에 오릅니다. |
| 삭제 시 발행 | 삭제하면 발행취소가 자동으로 함께 일어납니다. 먼저 발행취소할 필요가 없습니다. | 발행 상태인 자료는 보통 먼저 발행취소한 뒤 삭제합니다. |
| 본인 프로필 | GET /me로 조회합니다(/spaces/{spaceId}/me가 아닙니다). | GET /me(현재 Weegloo User). |
| Content Type | 생성·수정 엔드포인트가 없습니다. 틀은 CMA 소관입니다. | Content Type을 생성·수정·발행합니다. |
ServiceUser.isAdmin이 true인 회원은 다른 회원의 자료를 삭제하는 것만 추가로 할 수 있습니다. 이는 역할이 허용하는 작업 위에 더해지는 권한이며, 다른 회원 자료에 대한 수정 권한이나 읽기 권한이 늘어나는 것은 아닙니다. 그마저도 그 회원의 ServiceUserRole이 허용하는 작업 범위 안에서만 가능합니다.
위 동작(소유 범위, 생성 자동 발행, 삭제 자동 발행취소,
isAdmin범위)의 자세한 설명은 ServiceUser 로그인 (개념)을 참조하세요.
내 정보 (Me)
GET /me는 현재 토큰의 ServiceUser 본인 정보를 돌려줍니다. CMA처럼 Space 경로를 거치지 않고 /me를 직접 호출합니다.
다음은 ServiceUser의 응답 구조입니다. sys에 신원과 가입 정보가 들어 있고, 본문에 nickname·avatarUrl 같은 표시용 속성이 들어 있습니다.
{
"sys": {
"id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ",
"type": "ServiceUser",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"provider": "google",
"email": "minji.kim@example.com",
"createdAt": "2026-06-17T09:12:03.114Z",
"updatedAt": "2026-06-17T09:12:03.114Z"
},
"nickname": "민지",
"avatarUrl": "https://lh3.googleusercontent.com/a/example-avatar",
"enableLogin": true,
"isAdmin": false
}주요 키:
sys.id: ServiceUser의 고유 식별자입니다. 회원이 만든 Content·Media의createdBy가 이 값을 가리킵니다.sys.type: 리소스 종류로, ServiceUser는 항상"ServiceUser"입니다.sys.space: 이 회원이 속한 Space를 가리키는 참조입니다.sys.provider: 로그인에 사용한 공급자입니다(예:google).sys.email: 로그인 공급자가 제공한 이메일 주소입니다.nickname: 회원의 표시 이름입니다(필수).avatarUrl: 프로필 이미지 주소입니다(선택).roleOverride: 이 회원에게만 다른 ServiceUserRole을 적용할 때 그 역할을 가리키는 참조입니다(선택). 설정하지 않으면 ServiceLogin의 기본 역할을 따릅니다.enableLogin: 로그인 허용 여부입니다.isAdmin:true이면 다른 회원의 자료를 삭제할 수 있습니다(위 CMA와 다른 점 참조).
수정은 nickname과 avatarUrl만 가능합니다. provider·email·isAdmin 같은 값은 회원이 직접 바꾸지 않습니다.
Content
Content는 Content Type이라는 틀을 따르는 개별 자료입니다. 옷가게 쇼핑몰이라면 "상품" Content Type을 따르는 상품 하나하나가 Content입니다. ACMA에서 회원은 Space와 Content Type에 묶인 경로(/spaces/{spaceId}/content-types/{contentTypeId}/contents)로 자기 Content를 다룹니다.
다음은 데모 Space의 "상품" Content 하나입니다.
{
"sys": {
"id": "3trmXRM3RqbgSnifyg7OGhwhlqvAvq",
"type": "Content",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } },
"publish": {
"version": 3,
"at": "2026-06-16T14:35:11.210Z",
"firstAt": "2026-06-15T15:16:20.180Z",
"counter": 3,
"by": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } }
},
"createdBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } },
"createdAt": "2026-06-15T15:16:12.151Z",
"updatedAt": "2026-06-16T14:35:11.210Z",
"updatedBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } },
"version": 4,
"status": "Published"
},
"fields": {
"price": { "ko-KR": 18000 },
"description": { "ko-KR": "이중 진공 단열로 보온·보냉이 오래갑니다. 500ml 대용량." },
"photo": {},
"productName": { "ko-KR": "스테인리스 텀블러 500ml" }
},
"metadata": { "tags": [] }
}주요 키:
sys.id: Content의 고유 식별자입니다. 단일 조회·수정·삭제 경로의{contentId}에 들어갑니다.sys.contentType: 이 Content가 따르는 Content Type을 가리키는 참조입니다.sys.status: 발행 상태입니다. ACMA에서 만든 Content는 곧바로 발행되어Published로 돌아옵니다.sys.version: 리소스 버전으로, 변경마다 1씩 올라갑니다.sys.publish: 발행 이력입니다(version·at·firstAt·counter·by).fields: 각 필드의 값을{ apiName: { locale: value } }형태로 담습니다. 위 예시는price·description·photo·productName을ko-KR로케일로 담았습니다. 값이 없는 필드(photo)는 빈 객체입니다.metadata.tags: 이 Content에 붙은 Tag 목록입니다.
목록 조회는 표준 쿼리 파라미터(limit·skip·next·prev·order·select·include)를 받고, 응답은 items 배열과 links(페이지 커서)를 함께 돌려줍니다.
Media
Media는 업로드한 파일 자산입니다(이미지·동영상·문서 등). 회원이 Media를 만들려면 먼저 Upload API로 파일을 올려 Upload를 받고, 그 Upload의 sys.id를 Media 생성 요청에 담습니다. Media도 ACMA에서는 생성과 동시에 자동으로 발행됩니다.
다음은 데모 Space에 올린 텀블러 사진 Media입니다.
{
"sys": {
"id": "3trmXRM3RqbgSnifyg7OGjUMsPV3uU",
"type": "Media",
"space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
"publish": {
"version": 1,
"at": "2026-06-15T15:17:30.825Z",
"firstAt": "2026-06-15T15:17:30.825Z",
"counter": 1,
"by": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } }
},
"createdBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } },
"createdAt": "2026-06-15T15:17:30.589Z",
"updatedAt": "2026-06-15T15:17:30.825Z",
"updatedBy": { "sys": { "id": "3trmXRM3RqbgSnifyg7PSrUm9k2BaZ", "type": "Refer", "targetType": "ServiceUser" } },
"version": 2,
"status": "Published"
},
"fields": {
"title": { "ko-KR": "스테인리스 텀블러 500ml 정면 컷" },
"description": { "ko-KR": "흰 배경에서 찍은 텀블러 정면 제품 사진입니다." },
"file": {
"ko-KR": {
"fileName": "tumbler.png",
"contentType": "image/png",
"mimeGroups": ["Image"],
"url": "https://weegloo-media.com/medias/HnQ32YiH/3uU/3trmXRM3RqbgSnifyg7OGjUMsPV3uU/ko-KR/1/tumbler.png",
"detail": { "size": 50847, "image": { "width": 900, "height": 900 } }
}
}
},
"metadata": { "tags": [] }
}주요 키:
sys.id: Media의 고유 식별자입니다. 단일 조회·삭제 경로의{mediaId}에 들어가고, Content가 사진을 참조할 때 가리키는 값입니다.sys.status: 발행 상태입니다. ACMA에서 만든 Media는 곧바로 발행되어Published로 돌아옵니다.fields.title·fields.description: 자산의 제목·설명을 로케일 맵으로 담습니다(선택).fields.file: 로케일별 파일 정보입니다(필수). 처리가 끝나면url(전달용 주소),mimeGroups,detail(크기·이미지 해상도 등)이 채워집니다.
Media 생성 요청의 fields.file에는 처리 전 상태의 Upload 참조를 담습니다. 각 파일 항목은 fileName·contentType·mimeGroups·state·upload를 모두 포함하며, state는 항상 "PENDING"으로, upload에는 Upload API로 받은 Upload의 sys.id를 넣습니다. 생성 직후 플랫폼이 파일을 처리하고, 처리가 끝나면 위 응답처럼 url과 detail이 채워집니다.
관련 문서
- Auth API: ACMA 호출에 쓰는 토큰을 발급받는 OAuth 흐름.
- ACDA: 회원에게 발행 콘텐츠를 전달하는 읽기 API.
- ServiceUser 로그인 (개념): ServiceLogin·ServiceUserRole 설정.
- Upload API: Media 생성 전 파일 업로드.
