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개, 업무 군집 순)
| # | id | ko | en | 화면 |
|---|---|---|---|---|
| 1 | organization | 조직·사용자 | Organization & Users | UserDesk — 조직 트리(재정렬) + 부서 편집 다이얼로그 + 사용자 목록/상세·수정·신규 통합 |
| 2 | user-groups | 사용자그룹 | User groups | UserGroupsDesk |
| 3 | settings | 포털 연동 설정 | Portal Integration | PortalSectionTabs + 2개 구간 |
| 4 | operations | 권한 현황 | Access Overview | OperationsDesk |
| 5 | admins | 관리자 계정 | Administrator Accounts | AdminAccountsDesk |
| 6 | model-gateway | 모델 게이트웨이 | Model Gateway | ModelGatewayDesk |
| 7 | documents | 문서 라이브러리 | Document Library | DocumentLibraryDesk |
- 파일 보관(
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§ion=integration&view=provisioning → PortalProvisioningDesk, 화면에 「포털 목록으로」로 돌아오는 길을 둔다). 구간으로 두면 같은 일(포털 등록)이 두 곳에 흩어져 다시 헷갈린다.
주소: /admin?tab=settings§ion=<구간> (자동 구축은 &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§ion=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)
- 죽은 자산 제거 — 렌더되지 않던
ThemeSettingsDesk·UiReleaseDesk·ThemeColorField(약 600줄)와 어떤 컴포넌트에도 전달되지 않던uiThemeReleases·uiPortalTargets·uiDeployments쿼리(관리자 화면 진입마다 불필요한 호출 3회), 미사용 로케일 키(nav.credentials·nav.uiRelease·hero.settings.*·hero.credentials.*), 미사용lib/uiCenterLink.ts를 제거했다. - 순서·이름 — 라벨을 로케일 단일 원천으로 통일하고(
tabs[].label제거), 업무 군집(조직·사용자 → 권한 → 포털 연동 → 보안·계정 → 플랫폼 서비스) 순으로 재배열했다.등록포털 관리→포털 연동 설정,연동 포털별 접속권한 설정→포털 접속 권한,전사 권한 현황→권한 현황으로 짧은 명사형으로 맞췄다(사이드 레일 폭 220px). - 항목 통합 —
portal-access·portal-provisioning메뉴를settings한 화면의 구간으로 접어 메뉴를 12 → 10 으로 줄였다(todo.md 舊포털 연동 설정 흐름 통합).
정리 이력 (2026-10-02)
- 등록포털 관리 화면 흐름 —
settings→integration구간은 구간 탭 바로 아래에 같은 모양의 단계 탭이 항상 겹쳐 보였다. 등록된 포털이 있으면 현황 지표와 편집(PortalSettingsDesk)을 먼저 보여주고, 신규 포털의 1→4 등록 절차는 토글(.portal-onboarding-toggle)로 접어 둔다. 2~4단계(키·배포·R2)는 기존 포털 관리에도 쓰이므로 토글 안에서 그대로 접근한다(숨김이 아니다). - 단계 세로 stepper — 단계(
PortalStepTabs)를 구간 탭과 모양이 겹치지 않도록 세로 stepper(aria-orientation="vertical", 좌측 3px 표시)로 렌더한다. (todo.md 舊세로형 번호 흐름) - 중복 안내 제거 — 빈 상태의 정적 1→4 안내(
.portal-new-flow마크업)는 단계 탭과 같은 내용이라 제거했다. CSS 규칙은common-typography.css·reference-portal-frame.css가 계약으로 고정하고 있어 남겨 둔다(별도 정리 대상). - 기존 포털 우선 — 등록된 포털이 없으면 기존처럼 1→4 단계 흐름을 기본 노출한다. (todo.md 舊
기존 포털 우선항목) - 좌측 메뉴 순서 —
포털 연동 설정(settings)을권한 현황(operations) 위로 올렸다. 좌측 메뉴는 조직·사용자 → 포털 연동 설정 → 권한 현황 → 보안·암호화 → 관리자 계정 → 플랫폼 서비스 순이었다. (directoryAdminStyles.test.ts가 순서를 고정; 보안·암호화 탭은 2026-10-17 제거 — 현재 7개) - 조직·사용자 통합 —
조직도(OrganizationDesk)와사용자(UserDesk) 메뉴를 한 화면으로 합쳤다. 통합 화면(organization탭)은 조직 트리(재정렬 포함) + 부서 편집 다이얼로그 + 사용자 목록/상세·수정·신규를 함께 제공한다. 메뉴는 10 → 9 로 줄고,?tab=users는LEGACY_PORTAL_TAB_ALIASES가organization으로 흡수한다(옛 주소 보존).OrganizationDesk·UserDepartmentTree는 제거했다. (directoryAdminStyles.test.ts·locale.test.ts갱신) - 등록 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)가 계속 사용하므로 유지한다.
- 서버:
- 포털 연동 설정 화면 antd(래퍼) 전환 — 1차 —
settings탭 화면이 콘솔 자체 클래스(admin-*·portal-*)로 그려져 antd 룩을 따르지 않던 문제를 감사하고 전환을 시작했다.- 감사: 설정 탭 8개 컴포넌트의 래퍼 대비 raw 마크업 비율 —
PortalIntegrationStatus0/1,PortalStepTabs0/0,PortalSectionTabs0/0,PortalSettingsDesk1/10,PortalApiKeyScopeDesk2/11,CredentialDeliveryDesk5/20,R2CredentialDesk4/13,PortalProvisioningDesk3/11. (참고: 전환 완료분OperationsDesk25/1,SecurityDesk15/1) - 전환 완료(1차):
PortalIntegrationStatus→OperationalMetricGrid·PortalSectionTabs→PortalTabs·PortalStepTabs→AdminSection+PortalButton+DataStatusBadge(a11yrole="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
Input0개 · raw<label>0개 ·admin-form0개. 남은 것은 도메인 레이아웃 클래스(settings-grid·form-two·portal-provisioning-*·login-branding-fields·portal-registration-bundle)와 미사용 CSS 정리뿐이다.
- 감사: 설정 탭 8개 컴포넌트의 래퍼 대비 raw 마크업 비율 —
- 조직·사용자 폼 전환(4차, 진행 중) — 설정 탭 밖의 입력 필드를 같은 방식으로 옮긴다.
- 전환 완료:
UserFields(사용자 상세·생성 공용 폼 — ID·이름·이메일 →PortalTextField, 부서장 역할·부서 →PortalSelect(부서는showSearch), 재직 여부·상태 →PortalSelect),UserProfileControls(적용 포털·화면 모드 →PortalSelect, 색상 코드 →PortalTextField, 개인 테마 저장·초기화 →PortalButton+PortalConfirm). - 남은 것:
UserDesk다이얼로그 셸(shadcnDialog→PortalDialog)과 그 안의admin-form·credential-box·quiet-action,AdminAccountsDesk폼 4개,OrganizationBulkDialog업로드 폼,OrganizationTree·UserDesk빈 상태(→AdminEmptyState),AdminAccessGate로그인 화면(브랜드 스타일 유지 여부 검토 필요).
- 등록된 애플리케이션 박스 폭 —
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에 없음) — 래퍼로만 쓴다.
- 조직·사용자 화면 정렬 수정 — ① 지표 타일 순서를 조직 → 등록 사용자 로 바꿨다(조직 원본을 먼저 읽는다). ② 조직을 고르면 오른쪽 사용자 목록이 가운데에 몰려 보이던 문제를 고쳤다. 원인은
.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.tsgetPlatformLaunchUrl이 담당한다(상대 경로·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.tsgetPortalProvisioningReadiness()→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).
검증
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 회귀).