= [Dev] Admin UI 권한별 메뉴 전체Admin과 SiteAdmin의 권한에 따라 `/Admin` 네비게이션 메뉴를 다르게 표시한다. == 네비게이션 구조 [[[#!text [전체 관리] ← 전체Admin만 표시. Sites 블록보다 위에 온다 Dashboard User RecentChanges AccessLog Crawler Cache S3 Browser API Keys Nickname Requests [Sites] ▼ AhaWiki (#1) ← 현재 사이트(request.host): 자동 펼침. 강조(filled)는 URL 의 site seq 를 따른다 Config Page Cache Permission AccessLog ← 전체Admin만 표시 SiteAdmins ← 전체Admin만 표시 ▶ OtherSite (#2) ← 전체Admin만 표시 (접힌 상태) ]]] 이 그림은 `app/assets/js/admin/navigation.jsx` 를 옮긴 것이다. 항목을 더하거나 이름을 바꾸면 여기도 바꾼다 — 2026-05-24 이후 넉 달 동안 이 그림은 없어진 "Meta목록" 을 그리고 있었고, 나중에 붙은 API Keys·Nickname Requests 는 없었다. * 현재 사이트는 `request.host`로 판별 (`/api/me`의 `currentSiteSeq` 활용) * SiteAdmin에게는 담당 사이트만 표시, 전체Admin은 전체 표시 == 구현 결과 === Backend * `GET /api/me` 추가: `loggedIn`, `seq`, `nickname`, `loginEmail`, `profileImageUrl`, `isAdmin`, `siteAdminSeqs`, `currentSiteSeq` 반환. 로그아웃 상태면 `{"loggedIn": false}` 만 돌려주고, 내비게이션은 `loggedIn` 이 참일 때만 site 목록을 가져온다 * `SiteAdmin.selectByUser(userSeq)` 추가 * `/api/Admin/Sites`: SiteAdmin은 담당 사이트만 반환하도록 수정 * `Admin.scala`: `isAdmin || isSiteAdmin` 기준으로 접근 허용 === Frontend * `AdminLayout`에서 `me` fetch 후 `Navigation`에는 prop으로, 각 페이지에는 `AdminContext.Provider`로 전달 * `SiteNavItem` 컴포넌트 분리로 사이트별 서브메뉴 재사용 * `me.isAdmin`이 false 이면 Dashboard 는 담당 사이트 목록만 보여준다. 전체Admin 전용 페이지는 프론트에서 막지 않는다 — API 의 403 이 `HTTP 403` 오류 문구로 보이거나(S3 Browser·API Keys·Nickname Requests) 빈 화면으로 남는다(User·RecentChanges·AccessLog·Crawler Cache). 2026-05-24 에 넣었던 접근 불가 메시지는 같은 날 뺐다 * Me API 로딩 중 네비게이션 skeleton 표시 == 설계 결정 실제 보안은 API 레벨(`isAdmin` / `isSiteAdmin`)에서 처리한다. 프론트 메뉴 분기는 UX 목적으로만 사용한다.