현재 사용법 (2026-09-10,
6fbaee7f): 플랫폼 운영 콘솔 · 기능 마스터. 아래는 작성 당시의 변경 설명이며 현재 전체 동작·재측정 결과를 뜻하지 않습니다.
system-admin — 공용 LNB 사이드바 레이아웃 전환 (2026-08-21)
시스템 관리 콘솔(tools/system-admin) 상단에 일렬로 있던 8개 탭이 admin 앱과 동일한 좌측 사이드바(LNB)로 이동하고, 본문이 화면 전체 폭을 사용하게 됩니다.
직관: 메뉴가 늘어날수록 상단 가로 공간이 부족해지는 구조를, admin이 이미 검증한 config 기반 공용 LNB 컴포넌트를 그대로 재사용해 해결합니다. 새 화면을 붙일 때 이제 config에 한 줄 추가하면 됩니다.
배경 / 지금 동작
system-admin은 조직 프로비저닝·플랫폼 계정 관리를 담당하는 독립 운영 콘솔입니다. 다른 앱들과 달리 SDK($store.hereby) 없이 Alpine.js만 사용하며, 공용 패키지에서는 다이얼로그와 아이콘 레지스트리만 가져다 씁니다.
기존 화면 구조는 다음과 같았습니다. app.ts의 tabs 배열(8개 항목)을 dashboard.html 상단의 가로 탭바(ds-tab-nav)가 렌더링하고, 클릭하면 switchTab(id)이 activeTab을 바꿔 각 탭 뷰의 x-show가 전환됩니다. 탭 8개가 그룹 없이 한 줄로 나열되어 메뉴 식별이 어렵고, 추가 여지가 없었습니다. 또한 각 탭 본문 래퍼에 max-w-3xl~max-w-6xl 상한이 걸려 있어 넓은 화면에서도 본문이 좁게 렌더링됐습니다.
admin 앱은 같은 문제를 공용 컴포넌트 lnbSidebar(packages/components/src/components/LnbSidebar.ts)로 해결하고 있습니다. 섹션·메뉴·아이콘을 담은 config 객체(LnbConfig)를 넘기면 좌측 다크 레일을 렌더링하는 컴포넌트로, 접힘(68px 레일)/펼침(208px) 전환과 접힘 상태의 플라이아웃·툴팁까지 내장합니다. 반응형 3단(모바일 드로어 / 태블릿 레일 강제 / PC 사용자 설정)은 컴포넌트가 아니라 각 앱이 구현하며, admin의 syncLnbViewport()가 기준 구현입니다.
구조 변화
변경 전
변경 후
메뉴 id는 기존 탭 id를 그대로 사용하므로 해시 라우팅(#organizations/...)과 각 탭 뷰의 x-show 조건, switchTab의 로드 분기는 변경되지 않습니다.
개념 흐름별 변경
메뉴를 config 기반 공용 LNB로 교체합니다. app.ts의 평면 tabs 배열을 3개 섹션(조직 / 플랫폼 / 기준 데이터)으로 묶은 lnbConfig로 대체하고, 한글 라벨과 공용 레지스트리 아이콘(LNB 안 중복 없음)을 부여합니다. 해시 복원 검증과 상단 타이틀 조회는 config를 평탄화한 menuItems getter가 담당합니다. main.ts는 lnbSidebar를 Alpine에 직접 등록합니다 — 앱들이 쓰는 등록 경로(createAuthComponentsPlugin)는 SDK 스토어를 전제하므로 이 툴에서는 쓸 수 없고, 컴포넌트 소스를 상대 경로로 가져옵니다(SDK 의존이 없는 순수 모듈이라 가능합니다). dashboard.html은 admin의 workspace 셸 패턴을 따릅니다: 브랜드(마스터 심볼 + "hereby System Admin" 텍스트 표기) + 접기 토글 헤더, 그 아래 ds-lnb__top에 공용 HTML 파셜을 빌드타임 include 합니다.
admin의 반응형 3단을 그대로 이식합니다. 뷰포트 폭에 따라 <768은 오프캔버스 드로어, 768–1279는 68px 아이콘 레일 강제, ≥1280은 사용자가 고른 펼침/접힘 복원(lnbPrefCollapsed)입니다. LNB 문서가 강제하는 가드 4종 — 드로어 닫기는 onSelect 시점에만, onToggleCollapse의 모바일 가드, 사용자 설정 보존, 모바일에서 접기 chevron 숨김 — 을 모두 포함합니다. 드로어 위치/전환 CSS는 디자인시스템 정본에 없어(반응형 셸은 앱 구현 영역) admin main.css의 해당 블록을 이 툴의 main.css로 이식했습니다.
모바일 햄버거는 래퍼 div에 md:hidden을 겁니다. 처음에는 버튼 자체에 md:hidden을 걸었으나 데스크톱에서도 버튼이 보이는 문제가 실제로 발생했습니다. 원인은 CSS 레이어입니다: .ds-btn의 display: inline-flex는 언레이어드 선언이고 Tailwind 유틸리티(md:hidden)는 @layer utilities 안에 있어, 순서·명시도와 무관하게 언레이어드가 항상 이깁니다. admin이 햄버거를 맨 div 래퍼로 감싸는 이유가 이것이며, 같은 패턴으로 정정하고 트랩을 주석으로 남겼습니다.
본문 폭 상한을 제거해 전폭을 사용합니다. 8개 탭 뷰의 페이지 래퍼 max-w-*를 모두 제거합니다. 좁은 화면 대응은 래퍼 min-width가 아니라 각 표의 기존 overflow-x-auto 컨테이너가 담당합니다 — 래퍼에 min-width를 주면 페이지 전체가 가로 스크롤되어 디자인시스템 규칙(넓은 콘텐츠는 자기 컨테이너 안에서 스크롤)에 어긋납니다. 셀 단위 말줄임용 max-w-* truncate와 모달 카드 폭은 본문 상한이 아니므로 유지합니다.
Alpine 타입 정비를 함께 합니다. 이 툴의 env.d.ts에 있던 declare module "alpinejs"(무타입 스텁)는 저장소 규칙이 금지하는 형태로, 공용 아이콘 소스를 타입체크에 포함시키자 실제로 컴파일 오류를 냈습니다. 스텁을 제거하고 @types/alpinejs를 devDependency로 추가해 tsc가 깨끗하게 통과합니다. 저장소 루트의 미사용 이미지 파일 1건 제거(chore)도 이 브랜치에 포함되어 있습니다.
검증
pnpm lint:check, tsc -p tools/system-admin, vite build, 디자인시스템 게이트 4종(check:modals / check:icons / check:dialogs / check:spinners) 모두 통과했습니다. 런타임 화면은 dev 서버(API + 운영자 로그인)에서 데스크톱 접기/펼침·태블릿 레일·모바일 드로어를 확인해야 합니다.