Phase 3 잔여 컴포넌트 리팩토링 실행 계획
1. 범위와 기준
메뉴 adapter와 icon facade는 구현되었으므로 Phase 3의 다음 작업은 이를 실제 shell과 관리자 화면에 연결하는 것이다. 저장소의 마스터 문서에서는 이 범위를 공식적으로 Phase 4와 Phase 5로 나누고 있다. 이 문서는 현재 작업 흐름에서 Phase 3의 잔여 작업을 하나의 실행 계획으로 묶되, 각 작업의 공식 범위를 함께 기록한다. 1
공통 원칙은 도메인 정책과 공개 API를 유지하고 렌더링 계층만 Ant Design으로 교체하는 것이다. menuManagement는 권한과 메뉴 유효성의 소유자로 유지한다. menuAdapter는 이미 생성된 navigation model을 Ant Design item model로 변환한다. PortalConfigProvider는 모든 전환 컴포넌트의 전역 설정 진입점으로 유지한다.
2. 현재 상태와 남은 대상
| 순서 | 대상 | 공식 범위 | 현재 구현 | 이번 단계의 목표 | 예상 규모 |
|---|---|---|---|---|---|
| 1 | PortalShell | 레이아웃·네비게이션 | 네이티브 aside, header, nav, input, button | Layout, Sider, Header, Content, Menu, Input.Search, Dropdown, Avatar 연결 | 중 |
| 2 | PortalAdminShell | 레이아웃·네비게이션 | PortalShell 조합과 관리자 전용 UI | 공통 shell과 adapter 재사용, 관리자 권한 경계 고정 | 중 |
| 3 | PortalThemePolicySettings | 관리자 설정 | native radio, checkbox, select, button | Card, Radio, Checkbox, Select, Button, Typography 전환 | 소 |
| 4 | productMarks | 아이콘·브랜드 | 제품별 mark 구현 | icon facade와 공통 spacing·typography 규칙 통합 | 소 |
| 5 | DirectoryManagement | 데이터·관리 화면 | native tabs, select, list, action button | Tabs, Select, Table, Empty, Alert, Spin 전환 | 대 |
| 6 | dataShells | 데이터·피드백 | native table, state panel, feedback | Table, Descriptions, Empty, Result, Skeleton, Spin, Alert, Tag 전환 | 대 |
| 7 | contentComponents | 공통 콘텐츠 | native field, table, dialog, tabs, tree | Input, Select, Table, Modal, Tabs, Tree, Space 전환 | 대 |
| 8 | assetAttachments | 첨부파일 | native list와 action | List, Typography, Button, Empty, 필요 시 Upload 전환 | 소 |
3. 작업 패키지별 계획
3.1 P0 — PortalShell 레이아웃 전환
PortalShell은 전체 전환의 기준 구현으로 삼는다. 먼저 기존 public props와 상태 전이를 테스트로 고정한다. 이후 내부 DOM을 Ant Design layout으로 교체한다. 2
| 단계 | 구현 내용 | 보존해야 하는 계약 | 검증 |
|---|---|---|---|
| 3.1.1 | 최상위 Layout과 Content 도입 | className, children, main content landmark | role="main", 기존 class 존재 여부 |
| 3.1.2 | sidebar를 Sider로 교체 | sidebarOpen, defaultSidebarOpen, onSidebarOpenChange | controlled/uncontrolled open 상태 |
| 3.1.3 | collapse를 Sider.collapsed로 연결 | collapsed, onToggleCollapse, railCollapsed 호환 | 접기·펴기 callback 횟수와 값 |
| 3.1.4 | navigation을 Menu로 교체 | active id, 2-depth 구조, onSelect | adapter output과 selected/open keys 비교 |
| 3.1.5 | top nav를 Header로 교체 | topNavStart, topNavEnd, locale action | header landmark와 slot 렌더링 |
| 3.1.6 | 검색을 Input.Search로 교체 | search value/change/submit, explore result select | Enter, 버튼, 키보드 submit |
| 3.1.7 | 계정 영역을 Avatar와 Dropdown으로 교체 | account menu, logout, pending 상태 | menu item callback과 logout disabled |
| 3.1.8 | mobile close 처리를 Drawer 또는 Sider breakpoint와 연결 | onClose, onMenuClick | mobile open/close와 Escape |
PortalShell에서는 메뉴의 권한 판정을 다시 수행하지 않는다. buildPortalNavigationItems와 portalNavigationToAntdMenuProps의 결과를 그대로 렌더링한다. URL router는 공통 UI가 소유하지 않으므로 href와 기존 callback을 유지한다.
3.2 P0 — PortalAdminShell 관리자 shell 전환
PortalAdminShell은 별도 layout을 복제하지 않고 PortalShell을 조합한다. 관리자 메뉴는 PortalMenuVisibility.ADMIN과 기존 관리자 권한 판정 결과만 사용한다. 3
구현 순서는 다음과 같다.
- 관리자 navigation 모델을 기존
buildPortalNavigationItems로 생성한다. - 관리자 전용 role switch와 account action을
Dropdown또는Segmented로 분리한다. - 관리자 권한이 없는 경우 관리자 menu item이 adapter 입력에 포함되지 않는지 보장한다.
- 관리자 화면의 header, breadcrumb, content slot은
PortalShellpublic props와 동일한 landmark 규칙을 사용한다.
회귀 테스트는 관리자와 일반 사용자 각각에 대해 메뉴 노출, 직접 URL 진입 시 shell 렌더링, role switch callback, logout pending을 검증한다.
3.3 P1 — PortalThemePolicySettings 설정 control 전환
테마 정책 계산과 저장 callback은 변경하지 않는다. 네이티브 입력만 Ant Design control로 교체한다. 4
| 기존 control | 목표 control | 보존 계약 |
|---|---|---|
| radio policy mode | Radio.Group | mode, allowUserOverride 동시 갱신 |
| theme checkbox | Checkbox.Group 또는 개별 Checkbox | selectable theme key set |
| default theme select | Select | 허용 목록 외 값 차단 |
| save button | Button | saving, disabled, onSave payload |
| section headings | Card, Typography | portal name, helper text, status label |
강제 정책에서는 선택 control을 disabled로 유지한다. 승인되지 않은 테마가 렌더링되지 않는지 검증한다.
3.4 P1 — productMarks와 icon facade 통합
PortalIcon과 PortalAntdIcon은 안정적인 portal icon key를 공유해야 한다. 제품 mark는 제품별 시각적 구분을 유지하되, 내부 layout은 Space, Typography, Ant Design icon component를 사용할 수 있도록 정리한다. 5
필수 검증 항목은 제품별 public export, compact/normal 형태, accessible label, currentColor 상속, light/dark 배경 대비다. 기존 제품 이름과 class contract를 변경하지 않는다.
3.5 P2 — DirectoryManagement 데이터 화면 전환
DirectoryManagement는 서버 adapter가 반환하는 pagination, organization, user 상태를 변경하지 않고 표시 계층만 교체한다. 6
initialTab과 visible tabs를Tabs의 controlled key로 연결한다.- organization/user filter를
Select와Input.Search로 연결한다. - 사용자 목록을
Table로 변환하고 row key는 기존 directory user id를 사용한다. - loading, empty, error 상태를
Spin,Empty,Alert로 통일한다. - organization selection callback의 payload와 호출 순서를 유지한다.
이 작업은 shell 전환 후 진행한다. shell과 directory table을 동시에 변경하지 않아야 회귀 원인을 분리할 수 있다.
3.6 P2 — dataShells 상태·데이터 표시 전환
DataListShell과 DataDetailShell부터 전환한다. 상태 컴포넌트는 별도 단계로 분리한다. 7
| 컴포넌트 | 1차 목표 | 상태 계약 |
|---|---|---|
DataListShell | Table, Empty, Spin | loading, empty, error, selected row |
DataDetailShell | Descriptions, Card | field order, missing value rendering |
DataStatePanel | Empty, Result, Alert | role 값과 title/description |
FeedbackMessage | Alert, Button | dismiss callback과 tone mapping |
ActionFeedback | Spin, Alert, Result | pending/success/error message |
DataStatusBadge | Tag | tone class와 accessible label |
기존 role="status"와 role="alert" 계약을 먼저 테스트하고 Ant Design의 내부 role이 이를 덮어쓰지 않도록 wrapper를 유지한다.
3.7 P3 — contentComponents 공통 primitive 전환
contentComponents는 public prop 수가 많으므로 한 번에 전환하지 않는다. 다음 순서로 진행한다. 8
PortalStack,PortalToolbar,PortalPanel,PortalCardPortalTextField,PortalTextArea,PortalSelectPortalTabs,PortalDataTablePortalDialogPortalTree
폼 control은 native value/onChange payload를 보존한다. PortalDialog는 open, onOpenChange, Escape, backdrop close, title id를 보존하고 내부만 Modal로 바꾼다. PortalTree는 controlled/uncontrolled expanded ids를 유지하고 Tree의 expandedKeys와 selectedKeys로 변환한다.
3.8 P3 — assetAttachments 첨부 목록 전환
AssetAttachmentList는 먼저 List와 Typography로 교체하고, 업로드 기능은 별도 prop이 명시된 경우에만 Upload를 도입한다. 현재 public API에 없는 업로드 callback을 새로 강제하지 않는다. 9
다운로드 URL, target/rel, delete callback, formatSize, empty label, custom icon renderer를 그대로 유지한다. 삭제 버튼은 Button danger로 교체하되 기존 aria-label을 보존한다.
4. 브랜치와 커밋 단위
각 작업 패키지는 하나의 검토 가능한 커밋 또는 PR 단위로 유지한다.
| 커밋 순서 | 권장 메시지 | 포함 범위 |
|---|---|---|
| 1 | test: lock portal shell contracts | PortalShell/AdminShell 기준선 테스트 |
| 2 | refactor: migrate portal shell to antd layout | PortalShell layout, menu, header |
| 3 | refactor: migrate admin shell to shared antd layout | PortalAdminShell |
| 4 | refactor: migrate theme policy controls to antd | PortalThemePolicySettings |
| 5 | refactor: align product marks with icon facade | productMarks, icon facade 연결 |
| 6 | refactor: migrate directory management controls | DirectoryManagement |
| 7 | refactor: migrate data shells to antd | dataShells |
| 8 | refactor: migrate content primitives to antd | contentComponents |
| 9 | refactor: migrate asset attachment list to antd | assetAttachments |
각 커밋은 pnpm --filter @hohyun94/portal-foundation... build, check, 관련 Vitest를 통과해야 한다.
5. 테스트 매트릭스
| 영역 | 테스트 케이스 |
|---|---|
| 메뉴 | 권한 필터, active key, open key, 2-depth, 외부 링크, callback |
| shell | desktop, collapsed, mobile, controlled/uncontrolled, Escape close |
| 관리자 | 일반 사용자 숨김, 관리자 노출, role switch, logout pending |
| 테마 정책 | forced, user choice, empty approval list, saving, invalid default |
| 데이터 | loading, empty, error, success, selected row, retry/dismiss |
| 접근성 | landmark, keyboard focus, aria label, status/alert role, dialog title |
| 테마 | light/dark, token color, currentColor icon, disabled contrast |
| 호환성 | public export, class name, callback payload, native value semantics |
6. 완료 기준
Phase 3 잔여 작업은 다음 조건을 모두 만족할 때 완료로 판정한다.
| 기준 | 완료 조건 |
|---|---|
| API | 기존 public export와 props가 유지된다. 변경이 필요한 경우 deprecated compatibility layer가 먼저 제공된다. |
| 정책 | 권한·메뉴·테마·directory adapter 로직은 UI 컴포넌트에 중복 구현되지 않는다. |
| UI | 대상 컴포넌트가 Ant Design primitives를 사용하고 portal class contract를 유지한다. |
| 접근성 | keyboard navigation, focus, landmarks, status/alert/dialog semantics가 테스트된다. |
| 품질 | build, typecheck, 전체 Vitest가 통과한다. |
| 시각 | light/dark, desktop/mobile, collapsed/admin 주요 조합을 확인한다. |
| 릴리스 | 각 커밋이 독립적으로 revert 가능하고 branch history에 목적이 명확하다. |