Skip to content

관리자 메뉴 진입·소유 규약 (ADMIN MENU CONVENTION) ​

항목내용
상태채택 (2026-09-20)
범위portal-foundation 을 소비하는 모든 포털의 관리자 메뉴 진입·표시·소유
결정소비 포털의 관리자 메뉴는 사이드바 roleSwitch 로 진입하고 PortalAdminShell 로 표시한다. 메뉴 소유는 사용자 메뉴 = 포털, 공통 관리자 메뉴 = 중앙(콘솔), 포털 관리자 메뉴 = 포털 로 나눈다.
대상ProcessCenter(참조 구현) · AgentCenter · GuideCenter · DocumentLibrary · lunchapp
예외컨트롤 플레인(PortalConsole · UiPlatform) — 자체 관리 UI 를 소유한다
관련ROUTING_CONVENTION.md(경로가 화면의 원천) · MANUAL_CONVENTION.md(매뉴얼 진입)

1. 메뉴 소유·공급 모델 ​

메뉴 영역소유/통제공급 주체렌더
사용자 메뉴포털포털 (로컬 정의 또는 포털 DB 관리 메뉴)PortalShell.navigationItems
관리자 메뉴 — 공통(owner: FOUNDATION)중앙(콘솔) 통제콘솔 (pull-and-cache)PortalAdminShell.commonAdminItems
관리자 메뉴 — 포털(owner: PORTAL)포털포털 (포털 특유 기능)PortalAdminShell.portalAdminItems
  • 공통 관리자 메뉴의 예: 사용자·조직·역할, 사용자 그룹, 용어 사전, 메뉴 관리, 역할·시스템, 적용 테마 설정처럼 플랫폼 기능을 소비 포털에서 쓰기 위한 관리 화면.
  • 포털 관리자 메뉴의 예: 프로세스 관리(ProcessCenter), 분류 정책·연계 동기화(AgentCenter)처럼 그 포털의 기능을 제어하는 화면.
  • 두 구간은 이미 공통 계약에 인코딩되어 있다: PortalAdminMenuItem.owner(FOUNDATION/PORTAL) · section(COMMON/PORTAL) · PortalAdminShell 의 두 입력(commonAdminItems/portalAdminItems).

원칙: 포털 특유 기능의 관리 화면은 중앙이 강제하지 않는다. 포털이 오너십을 갖고 구성하며, 중앙은 공통(플랫폼) 관리 화면만 공급한다.

2. 진입·표시 규약 ​

  1. 진입은 사이드바 roleSwitch — PortalShell.roleSwitch={{ mode, canAccessAdmin, onSwitch, userLabel, adminLabel }}. canAccessAdmin 은 roles.includes("ADMIN") 처럼 포털의 관리자 판정을 그대로 넘긴다.
  2. 표시는 PortalAdminShell — 관리자 메뉴는 단일 레벨 목록으로 렌더한다(그룹/2 Depth 는 업무 트리와 혼동되므로 만들지 않는다).
  3. 되돌아가기 — PortalAdminShell 의 roleSwitch(mode: "admin")로 포털 화면에 돌아온다.
  4. URL — 관리자 화면은 경로를 갖는다(/admin/*). 경로가 화면의 원천이라는 ROUTING_CONVENTION.md 를 따른다.
  5. 직접 접근 차단 — 관리자 경로를 직접 열어도 서버가 권한을 강제한다. 화면은 보조로만 숨긴다.
  6. 매뉴얼 — 매뉴얼 화면은 메뉴 항목이 아니라 셸 슬롯으로 진입한다(PortalShell/PortalAdminShell 의 manualLink). 자리·모양은 셸이 소유한다 — MANUAL_CONVENTION.md.

구현 예시 ​

tsx
// 사용자 화면
<PortalShell
  navigationItems={buildNavigationItems(activeView, roles)}
  roleSwitch={canAccessAdmin ? {
    mode: "user",
    canAccessAdmin: true,
    onSwitch: () => navigate(ADMIN_HOME_PATH),
    userLabel: "포털",
    adminLabel: "관리자",
  } : undefined}
>
  {routes}
</PortalShell>

// 관리자 화면
<PortalAdminShell
  commonAdminItems={adminMenu.commonAdminItems}   // owner: FOUNDATION
  portalAdminItems={adminMenu.portalAdminItems}   // owner: PORTAL
  activeAdminMenuId={activeView ?? undefined}
  adminRole="ADMIN"
  onReturnToPortal={() => navigate(HOME_PATH)}
>
  {adminRoutes}
</PortalAdminShell>

3. 게이트 규칙 ​

지점규칙
사용자 메뉴 항목항목별 roles 로 거른다. 미지정이면 모든 인증 사용자, ADMIN 은 항상 통과. 자식이 모두 숨겨진 그룹은 렌더하지 않는다
관리자 영역 진입관리자 판정(canAccessAdmin)이 참일 때만 진입 경로를 노출한다
관리자 메뉴 항목PortalAdminShell 의 requiredRole === adminRole 로 거른다
중앙 관리 메뉴(관리형 모델)PortalMenuItem.visibility: "ADMIN" + accessRule(roles/scopes/groupIds) 로 buildPortalNavigationItems(items, { subject }) 가 거른다
서버화면 노출과 무관하게 서버가 최종 강제한다(버튼 숨김으로 권한을 대체하지 않는다)

4. 메뉴 소스의 3단계와 이전 경로 ​

단계정의현재 예
L1 로컬 정의포털 클라이언트 코드의 배열관리자 메뉴(모든 포털), AgentCenter 사용자 메뉴
L2 포털 DB 관리포털 자기 DB + 시드·검증(menuManagement 계약)ProcessCenter 사용자 메뉴(portalMenuRouter)
L3 콘솔 중앙 관리콘솔이 공통 관리자 메뉴를 배포(GET /api/directory/v1/menu)AgentCenter 공통 관리자 메뉴(공급 경로 구현 · 저장·저작은 후속)

이전 경로: L1 → L2 → L3. 어느 단계에서도 렌더러는 동일하다(PortalShell / PortalAdminShell). 공급처(로컬 배열 → 포털 DB → 콘솔 캐시)만 바뀌므로 이전 비용이 작다.

  • L1 → L2: 메뉴 정의를 포털 DB 로 옮기고 시드·검증을 붙인다.
  • L2 → L3: 공통(FOUNDATION) 관리자 항목만 콘솔 캐시에서 채우고, 포털(PORTAL) 항목은 그대로 포털에 둔다.
  • L3 에서도 콘솔 장애 시 마지막 정상 캐시/로컬 시드로 사용자·관리자 메뉴를 유지한다(빈 화면 금지).
  • L3 소비 계약: 콘솔 docs/portal-admin-menu-supply.md(요청·응답·인증). 이식 시 서버에 공급원(조회·캐시·폴백)을 두고 클라이언트에는 결과만 전달한다 — API 키를 브라우저로 내려보내지 않는다.

L3 소비 구현 예 (AgentCenter) ​

요소위치
계약·폴백 시드shared/adminMenu.ts (ConsoleAdminMenuItem, seedCommonAdminItems)
공급원(조회·캐시·폴백)server/adapters/identity/consoleAdminMenu.ts
클라이언트 노출trpc.system.adminMenu (인증 필요)
셸 반영buildAdminMenu(consoleItems 또는 null) → PortalAdminShell.commonAdminItems

null(아직 못 받음·콘솔 미설정)에서만 로컬 시드를 쓰고, 중앙이 빈 배열을 주면 그대로 반영한다(공급 주체가 중앙이므로 포털이 항목을 되살리지 않는다).

5. 소비 포털 이식 체크리스트 ​

  1. 사용자 메뉴 항목에 roles 를 지정했는가(미지정 = 전체 인증 사용자).
  2. 관리자 메뉴를 owner: FOUNDATION / owner: PORTAL 로 나눴는가.
  3. 관리자 진입을 roleSwitch 로 노출하고 canAccessAdmin 을 서버 판정과 일치시켰는가.
  4. 관리자 화면 경로를 라우팅 표에 등록했는가(직접 접근 시 서버 권한 검사 포함).
  5. PortalAdminShell 에 두 구간을 모두 넘겼는가(공통이 비어도 빈 배열로 명시).
  6. 매뉴얼 진입을 셸 슬롯(manualLink)으로 넘기고 메뉴 정의에서는 뺐는가 — MANUAL_CONVENTION.md.

6. 검증 ​

  • 소비 포털 테스트에서 역할별 메뉴 노출(VIEWER/OWNER/검토자/ADMIN)과 관리자 셸 common/portal 구간을 화면 없이 검증한다.
  • L3 를 쓰는 포털은 공급 경로도 검증한다 — 중앙 응답 채택·TTL·장애 시 캐시/시드 폴백·portalId 불일치 거부·셸 부적합 항목 필터(예: AgentCenter tests/unit/consoleAdminMenu.test.ts).
  • 배포 후 실제 화면에서 관리자 판정별 사이드바·관리자 셸을 확인한다.