Skip to content

PortalConsole 관리자 메뉴 (콘솔 자체 메뉴) ​

목적 ​

이 문서는 PortalConsole 이 자기 관리자 셸에 렌더하는 메뉴의 정의 위치·순서 규칙·정리 이력을 적는다. 소비 포털에 공통 관리자 메뉴를 공급하는 계약은 portal-admin-menu-supply.md 다 — 둘은 다른 문제다.

  • 소유·진입 규약: portal-foundation docs/ADMIN_MENU_CONVENTION.md (§예외: 컨트롤 플레인은 자체 관리 UI 를 소유한다)
  • 셸 렌더 규칙: 관리자 메뉴는 단일 레벨 목록이다(그룹/2 Depth 금지). 그래서 메뉴 수를 줄이려면 항목이 아니라 화면 안쪽 구조를 접어야 한다.

정의 위치 (단일 원천) ​

무엇어디
메뉴 항목 id·아이콘·순서client/src/pages/DirectoryAdmin.tsx tabs 배열
메뉴 라벨client/src/lib/locale.ts nav.* (ko/en) — tabMessageKeys 가 id → 키를 잇는다
셸 주입PortalAdminMenuItem[](owner: "FOUNDATION", section: "COMMON") → PortalAdminShell.commonAdminItems
화면 안쪽 구간PORTAL_SECTIONS + PortalSectionTabs (같은 파일)

라벨을 tabs 배열에 다시 넣지 않는다. 라벨을 두 곳에 두면 메뉴 이름이 갈라진다(directoryAdminStyles.test.ts 가 tabList 에 label: 이 없는지 검사한다).

현재 메뉴 (7개, 업무 군집 순) ​

#idkoen화면
1organization조직·사용자Organization & UsersUserDesk — 조직 트리(재정렬) + 부서 편집 다이얼로그 + 사용자 목록/상세·수정·신규 통합
2user-groups사용자그룹User groupsUserGroupsDesk
3settings포털 연동 설정Portal IntegrationPortalSectionTabs + 2개 구간
4operations권한 현황Access OverviewOperationsDesk
5admins관리자 계정Administrator AccountsAdminAccountsDesk
6model-gateway모델 게이트웨이Model GatewayModelGatewayDesk
7documents문서 라이브러리Document LibraryDocumentLibraryDesk
  • 파일 보관(file-store) 탭은 2026-10 에 없앴다 — 저장 라우팅·백엔드 자격증명은 FileStore 자체 /admin 이 소유하고, 콘솔은 레일 하단 바로가기로 연다(아래 「관리자 셸의 부가 진입점」). 화면 자산(FileStoreDesk·fileStoreDesk.css·nav.fileStore)은 되돌리기 쉽게 남겨 둔다.
  • 온톨로지(ontology) 탭은 2026-10-17 P4 표면 전환에서 없앴다 — 용어·관계 편집은 OntologyService 자체 관리자 화면이 소유하고, 콘솔은 등록된 ontology 포털 주소로 레일 하단 바로가기만 제공한다. 화면 자산(OntologyDesk·ontologyDesk.css·nav.ontology)은 롤백을 위해 남겨 둔다.
  • 보안·암호화(security) 탭은 2026-10-17 없앴다 — 조작 없이 상태·운영 원칙·관측 이력만 보여 주는 읽기 전용 화면이었다. 키 발급에 필요한 암호화 상태는 STEP 2 키 등록의 암호화 ON/암호화 설정 필요 배지로 흡수하고, 운영 원칙·이력은 PORTAL_KEY_REGISTRATION.md·감사 로그·서버 API(settings.encryptionStatus·settings.encryptionHistory)로 위임한다.

포털 연동 설정의 내부 구간 (PORTAL_SECTIONS) ​

관리자 셸이 단일 레벨이므로 메뉴를 늘리는 대신 한 화면 안 구간으로 접었다. 구간은 둘이다(2026-10-15 정리).

구간내용화면
integration (기본)STEP 1 연동 포털 등록 목록 + STEP 2 키 등록 목록(기존 키 등록/신규등록 팝업)PortalSettingsDesk (STEP 1) + PortalApiKeyScopeDesk (STEP 2)
access사용자별 포털 접속·관리자 권한PortalAccessDesk

「신규 포털」의 두 가지 길은 구간이 아니라 진입 방식이다 — 「신규 등록」(직접 등록, 같은 화면의 팝업)과 「자동 구축」(?tab=settings&section=integration&view=provisioning → PortalProvisioningDesk, 화면에 「포털 목록으로」로 돌아오는 길을 둔다). 구간으로 두면 같은 일(포털 등록)이 두 곳에 흩어져 다시 헷갈린다.

주소: /admin?tab=settings&section=<구간> (자동 구축은 &view=provisioning).

STEP 1 연동 포털 등록 목록의 열은 포털 종류 · 포털명 · 식별자 · 기본 URL · 상태 · 포털 호출 키 다(2026-10-17). callback 주소는 목록에서 빼고 행을 열어 수정하며, 종류 열이 소비포탈·플랫폼 서비스·플랫폼 컨트롤 플레인을 구분하므로 포털명 아래의 색상 배지는 두지 않는다.

통합 전·재구성 전 주소는 잃지 않는다 — LEGACY_PORTAL_TAB_ALIASES 가 ?tab=portal-access → settings+access, ?tab=portal-provisioning → settings+integration+view=provisioning 으로 흡수하고, LEGACY_PORTAL_SECTION_ALIASES 가 옛 ?tab=settings&section=provisioning 도 같은 자동 구축 화면으로 흡수한다.

용어 단일 원천 (2026-10-15) ​

포털 연동 설정의 개념 이름은 한 곳에만 둔다 — 화면이 같은 키를 일곱 이름으로 부르던 문제(실측 2026-10-15)를 계약으로 막는다.

무엇어디
개념 이름(포털 호출 키·중앙 발급·기존 키 등록·자동 전달·관리자 키·R2 자격증명)client/src/lib/portalIntegrationTerms.ts PORTAL_TERMS
쓰지 않는 옛 이름같은 파일 LEGACY_PORTAL_TERM_RULES
계약 검사(화면 문구 + 관리자용 서버 메시지 7개 파일)client/src/lib/portalIntegrationTerms.test.ts
용어 기준 문서docs/PORTAL_INTEGRATION_FLOW_AUDIT_2026-08-25.md §키 용어 기준

라벨·제목처럼 용어가 곧 값인 곳은 상수를 쓰고, 문장 안에서는 정식 이름으로 쓴다. 옛 이름이 다시 들어오면 계약 검사가 파일·줄·대체 이름을 함께 보여 주며 막는다.

정리 이력 (2026-09-30) ​

  1. 죽은 자산 제거 — 렌더되지 않던 ThemeSettingsDesk·UiReleaseDesk·ThemeColorField(약 600줄)와 어떤 컴포넌트에도 전달되지 않던 uiThemeReleases·uiPortalTargets·uiDeployments 쿼리(관리자 화면 진입마다 불필요한 호출 3회), 미사용 로케일 키(nav.credentials·nav.uiRelease·hero.settings.*·hero.credentials.*), 미사용 lib/uiCenterLink.ts 를 제거했다.
  2. 순서·이름 — 라벨을 로케일 단일 원천으로 통일하고(tabs[].label 제거), 업무 군집(조직·사용자 → 권한 → 포털 연동 → 보안·계정 → 플랫폼 서비스) 순으로 재배열했다. 등록포털 관리 → 포털 연동 설정, 연동 포털별 접속권한 설정 → 포털 접속 권한, 전사 권한 현황 → 권한 현황 으로 짧은 명사형으로 맞췄다(사이드 레일 폭 220px).
  3. 항목 통합 — portal-access·portal-provisioning 메뉴를 settings 한 화면의 구간으로 접어 메뉴를 12 → 10 으로 줄였다(todo.md 舊 포털 연동 설정 흐름 통합).

정리 이력 (2026-10-02) ​

  1. 등록포털 관리 화면 흐름 — settings → integration 구간은 구간 탭 바로 아래에 같은 모양의 단계 탭이 항상 겹쳐 보였다. 등록된 포털이 있으면 현황 지표와 편집(PortalSettingsDesk)을 먼저 보여주고, 신규 포털의 1→4 등록 절차는 토글(.portal-onboarding-toggle)로 접어 둔다. 2~4단계(키·배포·R2)는 기존 포털 관리에도 쓰이므로 토글 안에서 그대로 접근한다(숨김이 아니다).
  2. 단계 세로 stepper — 단계(PortalStepTabs)를 구간 탭과 모양이 겹치지 않도록 세로 stepper(aria-orientation="vertical", 좌측 3px 표시)로 렌더한다. (todo.md 舊 세로형 번호 흐름)
  3. 중복 안내 제거 — 빈 상태의 정적 1→4 안내(.portal-new-flow 마크업)는 단계 탭과 같은 내용이라 제거했다. CSS 규칙은 common-typography.css·reference-portal-frame.css 가 계약으로 고정하고 있어 남겨 둔다(별도 정리 대상).
  4. 기존 포털 우선 — 등록된 포털이 없으면 기존처럼 1→4 단계 흐름을 기본 노출한다. (todo.md 舊 기존 포털 우선 항목)
  5. 좌측 메뉴 순서 — 포털 연동 설정(settings)을 권한 현황(operations) 위로 올렸다. 좌측 메뉴는 조직·사용자 → 포털 연동 설정 → 권한 현황 → 보안·암호화 → 관리자 계정 → 플랫폼 서비스 순이었다. (directoryAdminStyles.test.ts 가 순서를 고정; 보안·암호화 탭은 2026-10-17 제거 — 현재 7개)
  6. 조직·사용자 통합 — 조직도(OrganizationDesk)와 사용자(UserDesk) 메뉴를 한 화면으로 합쳤다. 통합 화면(organization 탭)은 조직 트리(재정렬 포함) + 부서 편집 다이얼로그 + 사용자 목록/상세·수정·신규를 함께 제공한다. 메뉴는 10 → 9 로 줄고, ?tab=users 는 LEGACY_PORTAL_TAB_ALIASES 가 organization 으로 흡수한다(옛 주소 보존). OrganizationDesk·UserDepartmentTree 는 제거했다. (directoryAdminStyles.test.ts·locale.test.ts 갱신)
  7. 등록 1~3단계 통합 · 4단계 분리 — 포털 URL 등록 · 호출 인증 키 · 배포 대상을 한 폼에서 트랜잭션으로 저장하고, 자격증명 발급·전달만 별도 단계로 남겼다.
    • 서버: settings.registerPortalBundle — upsertPortalConfiguration + registerManagedPortalApiKey + saveCredentialDeliveryTarget 을 하나의 db.transaction 으로 묶는다. 이를 위해 세 헬퍼가 options?: { tx?: any } 를 받아 자체 트랜잭션을 열지 않을 수 있게 했다(중첩 트랜잭션 방지). 발급 키 secret 은 이때 한 번만 반환한다. (server/portalRegistrationBundle.contract.test.ts 가 계약 고정)
    • 화면: PortalStepTabs 를 4단계 → 2단계(01 포털 등록·설정 · 02 중앙 발급·전달)로 접었다. 01단계는 PortalSettingsDesk 한 폼에서 포털 정보 + 키 scope + (선택) 배포 대상을 함께 저장하고, 02단계는 CredentialDeliveryDesk·R2CredentialDesk 로 발급·전달만 실행한다.
    • 근거: 등록·키는 순수 upsert(부작용 없음)라 원자 저장이 가능하다. 반면 발급·전달은 외부 호출 · 1회 비밀값 표시 · health 대기(최대 8초)가 있어 저장 트랜잭션에 넣을 수 없다.
    • registerPortal(키 자동 발급)은 내부 등록 라우트(syncPortalRegistration)가 계속 사용하므로 유지한다.
  8. 포털 연동 설정 화면 antd(래퍼) 전환 — 1차 — settings 탭 화면이 콘솔 자체 클래스(admin-*·portal-*)로 그려져 antd 룩을 따르지 않던 문제를 감사하고 전환을 시작했다.
    • 감사: 설정 탭 8개 컴포넌트의 래퍼 대비 raw 마크업 비율 — PortalIntegrationStatus 0/1, PortalStepTabs 0/0, PortalSectionTabs 0/0, PortalSettingsDesk 1/10, PortalApiKeyScopeDesk 2/11, CredentialDeliveryDesk 5/20, R2CredentialDesk 4/13, PortalProvisioningDesk 3/11. (참고: 전환 완료분 OperationsDesk 25/1, SecurityDesk 15/1)
    • 전환 완료(1차): PortalIntegrationStatus → OperationalMetricGrid · PortalSectionTabs → PortalTabs · PortalStepTabs → AdminSection+PortalButton+DataStatusBadge(a11y role="tablist"·aria-orientation="vertical"·aria-current 유지) · PortalSettingsDesk → AdminSection·PortalDataTable·PortalDialog·PortalConfirm(DOM 주입 삭제 버튼 + window.confirm 제거)·AdminFeedback·PortalButton.
    • 전환 완료(2차): PortalApiKeyScopeDesk·CredentialDeliveryDesk·R2CredentialDesk — admin-list·admin-list-row → PortalDataTable, admin-empty → AdminEmptyState, settings-grid → PortalStack+AdminSection, admin-form·Input → PortalTextField/PortalSelect/PortalCheckbox, quiet-button/quiet-action → PortalButton, window.confirm → PortalConfirm, settings-security-note → AdminFeedback. PortalProvisioningDesk — 루트 <section> → PortalStack, 준비 상태·생성 이력 → AdminSection+PortalDataTable, 1회 비밀값 패널 → AdminSection+PortalTextField, 안내·오류 → AdminFeedback.
    • 전환 완료(3차): PortalSettingsDesk 다이얼로그 입력 필드(포털 정보·로그인 안내·배포 대상 — Input/<textarea> → PortalTextField/PortalTextArea/PortalSelect, 체크박스 → PortalCheckbox, admin-form 스킨 제거 후 PortalStack), PortalProvisioningDesk 마법사 입력 필드(식별자·표시 이름·온프레미스 URL/대상·GitHub 저장소 4종 → PortalTextField, provider 라디오 → PortalCheckbox, credential-box → PortalDescriptions, 확인 체크 → PortalCheckbox, admin-form → PortalStack).
    • 결과: 설정 탭 8개 컴포넌트에서 shadcn Input 0개 · raw <label> 0개 · admin-form 0개. 남은 것은 도메인 레이아웃 클래스(settings-grid·form-two·portal-provisioning-*·login-branding-fields·portal-registration-bundle)와 미사용 CSS 정리뿐이다.
  9. 조직·사용자 폼 전환(4차, 진행 중) — 설정 탭 밖의 입력 필드를 같은 방식으로 옮긴다.
  • 전환 완료: UserFields(사용자 상세·생성 공용 폼 — ID·이름·이메일 → PortalTextField, 부서장 역할·부서 → PortalSelect(부서는 showSearch), 재직 여부·상태 → PortalSelect), UserProfileControls(적용 포털·화면 모드 → PortalSelect, 색상 코드 → PortalTextField, 개인 테마 저장·초기화 → PortalButton+PortalConfirm).
  • 남은 것: UserDesk 다이얼로그 셸(shadcn Dialog → PortalDialog)과 그 안의 admin-form·credential-box·quiet-action, AdminAccountsDesk 폼 4개, OrganizationBulkDialog 업로드 폼, OrganizationTree·UserDesk 빈 상태(→ AdminEmptyState), AdminAccessGate 로그인 화면(브랜드 스타일 유지 여부 검토 필요).
  1. 등록된 애플리케이션 박스 폭 — settings → integration 의 등록된 애플리케이션 박스가 .settings-grid(2열: minmax(320px,.86fr) minmax(420px,1.14fr))의 왼쪽 열에만 갇혀 오른쪽이 비어 있었다. 그리드의 두 번째 자식이 PortalDialog 뿐인데, 닫혀 있을 때 오버레이라 아무것도 렌더하지 않아 오른쪽 열이 빈 채로 남는다. .settings-grid 래퍼를 제거해 박스가 전체 폭을 쓰게 했다.
  • 정리 대상 CSS(이제 미사용): .portal-step-tab*·.portal-step-tabs-head*·.portal-integration-metrics·.portal-section-tabs-list·.portal-section-tab*.
  • antd 는 직접 의존이 아니다(package.json 에 없음) — 래퍼로만 쓴다.
  1. 조직·사용자 화면 정렬 수정 — ① 지표 타일 순서를 조직 → 등록 사용자 로 바꿨다(조직 원본을 먼저 읽는다). ② 조직을 고르면 오른쪽 사용자 목록이 가운데에 몰려 보이던 문제를 고쳤다. 원인은 .user-listing-row(grid·stretch) 안에서 .pf-admin-section 이 display:grid 인데 align-content 가 기본(stretch)이라, 패널이 조직 트리 높이만큼 늘어난 만큼 머리말 행이 함께 커지며 본문(목록)을 아래로 밀어낸 것이다. align-content:start + 본문 flex-direction:column; justify-content:flex-start 로 머리말은 제 높이만 차지하고 목록이 위에서부터 쌓이게 고정했다(directoryAdminStyles.test.ts 가 계약으로 고정).

정리 이력 (2026-10-15) ​

제보: "포털 연동 설정은 볼 때마다 헷갈린다." 실측한 원인은 다섯이었고, 그 순서로 6단계를 커밋 단위로 정리했다.

원인(실측)정리
같은 키를 만드는 경로가 셋(01단계 번들 발급 dsvc_ · 기존 키 등록 · 중앙 발급 dpk_)이고 셋 다 portal_api_key_settings 에 쌓여 키 목록에 섞였다4단계 — 회전 규칙(retireSupersededPortalCallKeys) 한 곳 + 관리자 명시 회전 「이 키만 남기기」
같은 것을 일곱 이름으로 불렀다(포털 호출 인증 키·호출 인증 키·호출 키·등록된 포털 호출 키·활성 중앙 발급 키·중앙 API key·DirectoryService API Key)1단계 — client/src/lib/portalIntegrationTerms.ts 단일 원천 + portalIntegrationTerms.test.ts 계약
등록 포털 유무·토글 상태에 따라 화면 구조가 달라졌다(키 섹션이 토글에 숨었다)2단계 — 상태 분기·단계 탭·온보딩 토글 삭제, ①→②→③ 고정 순서
「자동 구축」이 별도 구간이라 같은 일이 두 곳에 흩어졌다3단계 — 구간 3 → 2, 자동 구축은 view=provisioning 진입 방식
배포 환경변수 이름 기본값이 세 곳에서 갈렸다5단계 — 정본 PORTAL_CONSOLE_API_KEY(shared/portalDelivery.ts)
상단이 안내문 4문장 + 지표 4개 + 콘솔 밖(UiPlatform) 안내의 연속이었다2단계 — 지표는 목록 열로, 안내는 섹션 설명으로 흡수(UiPlatform 안내문은 삭제, 레일 바로가기는 유지)

전체 결정·근거·검증 기록: PORTAL_INTEGRATION_REWORK_2026-10-15.md.

관리자 셸의 부가 진입점 (메뉴가 아닌 것) ​

메뉴 수를 늘리지 않기 위해, 메뉴가 아닌 진입점은 셸의 다른 슬롯을 쓴다.

진입점슬롯조건
UiPlatform(공통 UI 원장) 바로가기PortalAdminShell → PortalShell.sidebarFooter (레일 하단)등록된 ui-center 포털이 enabled 이고 baseUrl 이 절대 HTTPS URL 일 때만
FileStore(파일 저장소) 바로가기같음 — 레일 하단, 새 창으로 연다등록된 file-store 포털이 enabled 이고 baseUrl 이 절대 HTTPS URL 일 때만
OntologyService(용어·온톨로지 원장) 바로가기같음 — 레일 하단, 새 창으로 연다등록된 ontology 포털이 enabled 이고 baseUrl 이 절대 HTTPS URL 일 때만
  • URL 검증은 client/src/lib/platformLaunchLink.ts getPlatformLaunchUrl 이 담당한다(상대 경로·javascript:·http: 거부). 세 플랫폼이 같은 런처 클래스 .platform-launch 와 룩을 쓴다(2026-10 에 ui-center-launch 에서 이름을 플랫폼 중립으로 바꿨다).
  • FileStore 운영(백엔드·라우팅 정책·자격증명 회전)은 콘솔 화면이 소유하지 않는다(2026-10 결정) — FileStore /admin 의 로컬 관리자 세션이 원천이고, 콘솔은 등록된 포털 주소로 넘긴다(docs/FILE_STORE_DESK.md).
  • OntologyService 운영(용어·관계·승인 이력)은 콘솔 화면이 소유하지 않는다(2026-10-17 P4) — OntologyService 자체 관리자 화면이 원천이고, 콘솔은 등록된 ontology 포털 주소로 넘긴다(docs/ontology-move-runbook.md §4).
  • 공통 UI 릴리스 승인·배포는 UiPlatform 이 소유하므로, 콘솔에는 원장·권한 API 만 두고 화면은 바로가기만 제공한다(todo.md 舊 공통 UI 배포 메뉴 항목).
  • 배치 결정: 舊 계획은 "좌측 레일의 로그인 사용자 정보 바로 위"였다. 그 자리는 舊 자체 셸(.rail-collapsed) 전용이었고, 지금 관리자 셸의 사용자 정보는 상단 내비게이션에 있다. 따라서 메뉴 의미를 흐리지 않는 레일 하단 슬롯을 쓴다.

자동 생성 준비 상태 점검 ​

포털 연동 설정 → 신규 포털 생성 구간은 제출 전에 서버 설정을 먼저 확인한다.

  • 서버: server/portalProvisioningReadiness.ts getPortalProvisioningReadiness() → settings.portalProvisioningReadiness 쿼리
  • 검사 항목: PORTAL_PROVISIONING_GITHUB_TOKEN / PORTAL_PROVISIONING_TEMPLATE_REPOSITORY(owner/repository 형식) / PORTAL_CONSOLE_PUBLIC_URL(HTTPS)
  • 값은 내려보내지 않는다 — 채워야 할 설정 이름과 형식 안내만 돌려준다(테스트로 고정).

남은 일 ​

  • 남은 고아 CSS: index.css 의 .ui-release-*·.ui-theme-preview*·.ui-target-* 규칙은 실사용 선택자(.ui-delivery-history)와 한 줄에 섞여 있어 이번 정리에서 제외했다. theme-settings-desk 블록과 navy-base.css 의 .ui-release-card 규칙은 제거했고, directoryAdminStyles.test.ts 가 이 클래스들이 마크업으로 돌아오지 못하게 막는다.
  • Home.tsx·MobileDirectory.tsx 는 라우터(App.tsx)에 등록되어 있지 않다. 존치로 결정한다 — MobileDirectory.tsx 는 데스크톱 CentralLogin·DirectoryAdmin 의 모바일 변형이고 자체 계약 테스트로 유지되고 있어, 화면 폭 기반 전환은 로그인 경로를 바꾸는 제품 결정이라 소유자 확인이 필요하다. (Home.tsx 의 랜딩 콘텐츠는 콘솔 hero·요약과 중복)
  • PortalConsole 자체 관리자 메뉴의 DB 저장·저작·감사(L3b)는 여전히 후속이다 — 검증 가능한 마이그레이션 환경이 필요하다(portal-admin-menu-supply.md).

검증 ​

bash
cd PortalConsole
pnpm check
pnpm exec vitest run client/src/lib/directoryAdminStyles.test.ts client/src/lib/locale.test.ts
  • 메뉴 정의·순서·단일 원천·구간 통합·기존 포털 우선 흐름: client/src/lib/directoryAdminStyles.test.ts
  • 로케일 키: client/src/lib/locale.test.ts

우측 프레임 레이아웃 (플레인 타입) ​

관리자 메뉴의 우측 프레임은 PortalAdminShell에 contentVariant="plain"으로 켠다(client/src/pages/DirectoryAdmin.tsx). 프레임은 셸이 소유하므로 메뉴마다 레이아웃을 다시 짜지 않는다 — 모든 메뉴의 AdminSection·PortalPanel·PortalCard가 공통 플레인 규칙(보더·그림자·둥근 모서리 제거, 구간은 헤어라인, 16px 리듬)을 따른다. 이 규칙은 저장소(FileStore) 화면에서 정의해 portal-foundation 공통으로 올린 PortalPanel variant="plain"과 같다.

버튼은 공통 표준(PORTAL_BUTTON_STANDARD — 높이 30px·글자 12px)이 antd 테마에 걸려 있어 PortalButton과 일반 버튼이 같은 모양을 쓴다. 조직 트리 토글·로그인 텍스트 링크처럼 액션이 아닌 레이아웃 요소만 원시 <button>을 유지한다.

섹션 장식 문구와 팝업 필드 안내 (2026-10) ​

  • 섹션 제목 위 작은 장식 문구(eyebrow)는 쓰지 않는다 — 화면을 어지럽혀 모두 제거했고, 회귀는 client/src/lib/directoryAdminStyles.test.ts 가 막는다(모바일 MobileSectionHeading 도 같다).
  • 포털 연결 설정 팝업은 별도 '앱에 설정할 값' 표를 두지 않는다. 항목별로 필수는 라벨 옆 빨간 별표(required → .pf-field__required), 설명은 라벨 아래 안내문(description)으로 붙인다. 포털 앱이 함께 넣어야 할 공유 값(PORTAL_CONSOLE_URL·PORTAL_JWT_SECRET)만 팝업 상단에 한 줄로 안내한다.

CSS 위생 ​

  • 죽은 규칙은 남기지 않는다. 점검은 pnpm css:purge-dead(dry-run) → node scripts/purge-dead-css.mjs --apply. 판정은 "소스·테스트 어디에도 클래스가 리터럴로 없는 규칙"만 대상으로 하고, 셀렉터에 살아있는 클래스가 섞이면 남긴다(오삭제 방지). pf-·ant- 접두는 라이브러리 소유라 제외한다.
  • 공통 버튼(PortalButton)을 콘솔 CSS로 덮지 않는다. 버튼 표준은 PORTAL_BUTTON_STANDARD 하나다. 요소 선택자로 원시 버튼을 꾸밀 때는 반드시 :not(.ant-btn) 을 붙인다(안 붙이면 팝업·폼의 공통 버튼까지 잡힌다 — 2026-10 회귀).