공통 관리자 메뉴 공급 계약 (GET /api/directory/v1/menu)
목적
소비 포털의 관리자 셸은 공통(FOUNDATION) 구간과 포털(PORTAL) 구간으로 나뉜다. 포털 구간은 그 포털이 소유하지만, 공통 구간(사용자·역할 등 플랫폼 기능)은 중앙이 소유한다. 이 문서는 그 공급 경로의 계약이다.
- 규약(무엇이 누구 소유인가): portal-foundation
docs/ADMIN_MENU_CONVENTION.md - 설계(왜 이 구조인가): portal-foundation
docs/MENU_GOVERNANCE_DESIGN.md§3 (L3 중앙 공급)
요청
GET {PORTAL_CONSOLE_ORIGIN}/api/directory/v1/menu?portalId=agent-center
authorization: Bearer <포털 API 키> # scope: directory:read- 포털 API 키(기계 인증)만 허용한다. 사용자 JWT 로는 관리자 메뉴를 정의할 수 없다(
403 api_key_required). - 키가 가리키는 포털과 다른
portalId를 요청하면 거절한다(403 portal_mismatch).portalId를 생략하면 키의 포털로 답한다. - scope 가 없으면
403 insufficient_scope다.
응답
json
{
"data": {
"portalId": "agent-center",
"version": "a1b2c3d4e5f6",
"items": [
{
"key": "admin-users",
"label": "사용자·역할",
"labelKo": "사용자·역할",
"labelEn": "Users & roles",
"type": "LINK",
"href": "/admin/users",
"iconKey": "Users",
"visibility": "ADMIN",
"sortOrder": 10,
"enabled": true,
"systemReserved": true,
"owner": "FOUNDATION"
}
]
},
"meta": { "requestId": "…", "generatedAt": "2026-09-20T00:00:00.000Z" }
}version은 내용 해시다. 포털은 이 값이 바뀌었는지로 갱신을 판단한다(현재는 DB 저장이 없어updatedAt대신 쓴다).href는 그 포털의 관리자 셸 안 경로(/로 시작)다. 플랫폼 기능이라도 화면 경로는 포털마다 다르다.- 항목이 없으면
items: []다 — 포털은 로컬 시드로 폴백한다(관리자 셸이 빈 채로 뜨지 않는다).
항목 정의 (현재)
| 설정 | 의미 |
|---|---|
내장 시드 server/portalAdminMenu.ts | 포털별 기본 항목(예: agent-center → 사용자·역할 /admin/users) |
환경변수 PORTAL_ADMIN_MENU_JSON | {"<portalId>": [ … ]} 로 내장 시드를 덮어쓴다. JSON 이 깨져 있으면 경고 후 내장 시드로 돌아간다 |
항목 검증: key·label 필수, LINK 는 href 가 / 로 시작해야 한다. 그 밖의 항목(경로 없는 LINK, 포털 밖 절대 URL, 라벨·키 누락)은 서버가 버리고 내려보내지 않는다.
하지 않는 것 (L3b 후속)
- DB 저장·저작 화면·감사: 현재는 내장 시드 + 환경변수다. 저장소를 붙이기 전에 스키마를 실행 검증할 수 있는 환경(마이그레이션·운영 DB)이 필요하다 — 검증 불가능한 마이그레이션을 먼저 넣지 않는다.
- 포털별 항목 편집 UI, 갱신 이력, 감사 로그.
- 메뉴 항목의
GROUP·EXTERNAL_LINK·DIVIDER는 계약에 있으나 현재 공급하지 않는다(포털 중립 셸 항목으로 좁혀서 전달한다).
검증
bash
cd PortalConsole
pnpm exec vitest run server/portalAdminMenu.test.ts server/portalAdminMenuRoute.test.ts- 정의·검증·정렬·버전:
server/portalAdminMenu.test.ts - 인증·scope·portalId 일치·빈 목록:
server/portalAdminMenuRoute.test.ts
소비 측(포털) 검증은 각 포털에 있다 — AgentCenter 는 tests/unit/consoleAdminMenu.test.ts(캐시·TTL·폴백·필터)와 tests/integration/router.test.ts(system.adminMenu).