Skip to content

포털콘솔 리팩토링 계획 (2026-10-17) ​

관리자 콘솔(DirectoryAdmin.tsx)과 스키마 계층의 구조 부채를 동작 변경 없이 줄이는 계획이다. 측정 근거와 우선순위는 2026-10-17 코드·테스트 실측에 기반한다.

측정 (실측) ​

지표값비고
규모284 files · 37,089 LOCserver·client·shared
최대 파일client/src/pages/DirectoryAdmin.tsx 4,905 → 951줄이번 슬라이스들로 3,954줄 분리(약 81%)
any/as unknown as96 (server·shared)이중 스키마 타입 캐스팅이 주 원인
tx: any5트랜잭션 공통 타입 부재
스키마 정의MySQL 616 + PG 618 + 어댑터 70 = 1,304줄같은 테이블을 두 번 정의
ensure-*.mjs9마이그레이션과 병존하는 두 번째 진화 경로
server 루트평면 72 files도메인 폴더 없음
테스트105 files · 574 통과 (2 skipped)안전망은 정상

원칙 ​

  1. 동작을 바꾸지 않는다 — 순수 이동·추출만. 동작 변경은 별도 커밋.
  2. 테스트를 먼저 통과시킨다 — 리팩토링 전후 모두 pnpm verify:full.
  3. 소스-계약 테스트를 함께 옮긴다 — client/src/lib/directoryAdminStyles.test.ts는 DirectoryAdmin.tsx를 readFileSync로 읽어 문자열을 검사한다. 컴포넌트를 옮기면 그 assert도 새 파일을 읽도록 같은 커밋에서 바꿔야 한다(아래 선례 참고).
  4. 한 번에 한 컴포넌트/한 도메인. 큰 diff를 한 커밋에 넣지 않는다.

단계 ​

P0 — DirectoryAdmin.tsx 분해 (진행 중) ​

client/src/pages/DirectoryAdmin.tsx는 관리자 앱 전체를 담은 단일 파일이다. 아래 순서로 client/src/features/ 또는 client/src/components/로 하나씩 옮긴다.

순서대상(현재 줄 범위 기준)이동 위치(제안)
✅ 1PortalKeyRegistrationGuide + 위치 예시 상수/snippetcomponents/PortalKeyRegistrationGuide.tsx
✅ 2PortalKeyIssueDialog (+ ScopePresetRow·scope 상수 → 공용 모듈)features/api-keys/PortalKeyIssueDialog.tsx · features/api-keys/portalScopes.tsx
✅ 3PortalApiKeyScopeDesk (+ formatLastUsed, 발급 팝업 호출부)features/api-keys/PortalApiKeyScopeDesk.tsx
✅ 4PortalAccessDeskfeatures/portal-access/PortalAccessDesk.tsx
✅ 5PortalSettingsDesk + PortalSectionTabs (+ PORTAL_SECTIONS·PortalSection)features/portal-settings/*
✅ 6OrganizationBulkDialog · OrganizationTreefeatures/organization/*
✅ 7UserDesk · UserFields · UserProfileControls (+ departmentHeadRoleMessageKeys·PERSONAL_THEME_ACCENTS)features/users/*
✅ 8OperationsDesk · AdminAccountsDeskfeatures/operations/*

P0 완료. DirectoryAdmin.tsx 에는 라우팅·권한 게이트·탭 조합·데스크 배치만 남는다(951줄).

각 단계마다:

bash
pnpm check
npx vitest run client/src/lib/directoryAdminStyles.test.ts
pnpm verify:full

선례(1·2단계에서 처리):

  • PortalKeyRegistrationGuide를 옮기면서 directoryAdminStyles.test.ts의 portal-key-registration-guide·portal-key-secret·PORTAL_CONSOLE_URL=·브라우저 코드… assert를 새 파일로 재지정했다. portalDeliverySecretName.contract·portalIntegrationTerms 계약 테스트도 같은 파일을 보도록 바꿨다.
  • PortalKeyIssueDialog를 옮기면서 ScopePresetRow와 scope 상수(11개·라벨·프리셋)를 features/api-keys/portalScopes.tsx로 함께 분리했다(등록 폼·scope 데스크·발급 팝업이 공유). trpc.settings.issuePortalCredential·secret: result.secret 계약은 발급 팝업 파일로 재지정.
  • PortalApiKeyScopeDesk(676줄)를 옮기면서 formatLastUsed도 함께 갔다. 발급 팝업은 이제 데스크가 렌더하므로 <PortalKeyIssueDialog·title="STEP 2 키 등록"·회전 문구 계약은 데스크 파일을 본다. 반대로 portalApiKeys.useQuery는 페이지가 소유한다(데스크에 props로 내려준다). directoryAdminStyles.test.ts에 portalApiKeyScopeDesk(정규화본 포함) 소스를 추가해 이동한 마크업 계약을 한 곳에서 검사한다.

P1 — 스키마 단일화 (별도 계획 필요, 리스크 중간~높음) ​

drizzle/schema.ts가 런타임에 PG 테이블을 고르면서 타입은 MySQL로 캐스팅한다 (selected.portalApiKeySettings as typeof mysql.portalApiKeySettings). 이 구조가 any/tx: any 96건의 뿌리다.

  1. PG를 정본으로 선언하고 MySQL은 생성/어댑터로 파생
  2. schema.ts의 교차 캐스팅 제거, 공통 DbTransaction 타입 도입
  3. 전 테이블 회귀(마이그레이션·조회·쓰기) — 배포 DB 스냅샷으로 검증

운영 DB 스키마가 걸린 작업이므로 감독 하에 진행한다. 자동 반영 금지.

P2 — 보정 스크립트 은퇴 (리스크 낮음) — 1단계 완료 ​

scripts/ensure-*.mjs 9개는 마이그레이션 저널 누락을 응급 보정하던 장치다. 저널 계약 테스트(server/migrationsJournal.contract.test.ts)가 생겼으므로:

  1. ✅ 1단계(완료): 9개 스크립트에 "진단·응급 보정용" 헤더를 넣어, 신규 스키마 변경은 마이그레이션 + 저널 등록으로 추가하도록 명시했다(동작 변경 없음).
  2. 2단계(남음): 각 스크립트를 진단 전용(현재 상태 출력, 변경 없음)으로 축소하거나 마이그레이션 + 계약 테스트로 흡수한 뒤 삭제한다. 온프레미스(폐쇄망) 배포 절차 문서와 함께 갱신해야 하므로 배포 절차를 아는 사람과 같이 진행한다.

P3 — 서버 모듈 경계 (리스크 낮음) ​

  • server/ 평면 72 files → server/portal/, server/credentials/, server/directory/
  • server/portalRoutes.ts(642줄) → 등록 함수 단위 분리 (registerPkceTokenRoute, registerPortalCryptoRoutes, registerPortalReadRoutes …)
  • 각 단계 후 server/portalRoutes.test.ts + pnpm check

하지 않을 것 ​

  • 전면 재작성 — 계층·테스트 자산을 버리는 이득이 없다.
  • 워크플로우 맵 재구성 — 이 저장소가 아니라 ProcessCenter의 과제다.
  • P1을 자동(무인)으로 배포 — DB 스키마 회귀 위험이 있다.

검증 명령 ​

bash
pnpm check            # tsc
pnpm test             # vitest 전체
pnpm verify           # check + build
pnpm verify:full      # test + check + build