Phase 4 데이터·콘텐츠 컴포넌트 리팩토링 계획
1. 범위와 공식 단계 매핑
현재 작업 흐름에서 Phase 3은 레이아웃, 메뉴, 관리자 shell, 테마 정책, 디렉터리 화면을 포함한다. 다음 Phase 4는 남은 공통 데이터·콘텐츠·첨부 컴포넌트를 Ant Design 기반으로 전환한다. 저장소의 마스터 계획에서는 이 범위가 공식적으로 Phase 5 데이터 표시·피드백 전환과 Phase 6 Form 및 업무 화면 전환에 해당한다. 1
Phase 4의 목표는 공통 데이터 상태와 콘텐츠 primitive의 렌더링 체계를 통일하는 것이다. 도메인 adapter, 권한 정책, public prop, callback payload는 유지하고, native table·input·dialog·tree·attachment action을 Ant Design primitive 조합으로 교체한다.
2. 현재 기준선
Phase 3 완료 상태에서는 다음 기반이 준비되어 있다.
| 기반 | 상태 | Phase 4에서의 사용 |
|---|---|---|
PortalConfigProvider | 완료 | Table, Modal, Input, Tree, Upload token 적용 |
PortalStatusBadge | Ant Design Tag 기반 | data status와 row status에 재사용 |
PortalEmptyState | Ant Design Empty 기반 | Empty, DataState, attachment empty 상태에 재사용 |
PortalButton | Ant Design Button 기반 | retry, delete, dialog action, attachment action에 재사용 |
DirectoryManagement | Tabs, Tree, Table, Select, Spin, Empty, Alert 전환 | Phase 4 데이터 상태 패턴의 기준 |
| 메뉴 adapter/icon facade | 완료 | 콘텐츠 action icon과 table action에 사용 |
3. 대상 컴포넌트 우선순위
| 우선순위 | 대상 | 주요 Ant Design primitive | 목표 |
|---|---|---|---|
| P0 | dataShells.tsx | Table, Descriptions, Card, Empty, Result, Skeleton, Spin, Alert, Tag | loading·empty·error·data 상태 표준화 |
| P0 | contentComponents.tsx의 field/table/tabs | Input, Input.TextArea, Select, Table, Tabs, Space | 공통 콘텐츠 primitive 전환 |
| P1 | contentComponents.tsx의 PortalDialog | Modal, Typography, Space | dialog focus·Escape·action 계약 보존 |
| P1 | contentComponents.tsx의 PortalTree | Tree | expanded/selected controlled API 보존 |
| P1 | assetAttachments.tsx | List, Typography, Button, Empty, 선택적 Upload | download/delete/empty 계약 보존 |
| P2 | PortalComponents.tsx 잔여 primitive | Breadcrumb, PageHeader 조합, Space | Phase 3에서 남은 시각·접근성 정리 |
| P2 | directory/data 공통 adapter | pagination·sorting·filter adapter | Table API와 서버 계약 분리 |
PortalAdminShell, PortalThemePolicySettings, DirectoryManagement는 Phase 4의 대상에서 제외한다. 해당 컴포넌트는 Phase 3에서 Ant Design 전환을 완료했으며, Phase 4에서는 회귀 테스트만 확장한다.
4. 목표 아키텍처
4.1 상태와 데이터 표시의 분리
dataShells는 데이터 fetching이나 권한 판단을 소유하지 않는다. 입력으로 받은 상태와 데이터를 Table, Descriptions, Empty, Result, Alert, Spin으로 표시한다. 서버 adapter는 기존 호출 계층에 남긴다.
도메인 adapter / query hook
│
▼
dataShell public props
│
├── loading → Skeleton / Spin
├── empty → Empty
├── error → Alert / Result
└── data → Table / Descriptions / Tag4.2 Content primitive의 compatibility layer
contentComponents.tsx의 기존 export와 prop은 유지한다. 각 primitive 내부에서 Ant Design value model을 native value model로 변환하는 얇은 adapter를 둔다. 예를 들어 PortalSelect는 Ant Design Select의 onChange(value, option)에서 기존 소비자가 기대하는 ChangeEvent 또는 value payload를 생성해야 한다. 이 변환이 불명확한 경우에는 기존 callback 타입을 우선하고, 새로운 callback은 별도 이름으로 추가한다.
4.3 Portal token과 Ant Design token의 단일 진입점
각 컴포넌트에서 임의의 색상과 spacing을 추가하지 않는다. PortalConfigProvider token을 사용하고, legacy pf-* class는 wrapper에 유지한다. Ant Design 내부 class를 직접 덮어쓰는 CSS는 component token 또는 wrapper descendant selector로 제한한다.
5. 작업 패키지별 실행 계획
5.1 Phase 4.0 — 공개 API와 상태 기준선 고정
구현 변경 전에 다음 계약을 테스트로 고정한다.
| 영역 | 고정할 계약 |
|---|---|
dataShells | 각 export의 prop, 상태 우선순위, role, callback |
contentComponents | field value, onChange, controlled/uncontrolled, class |
PortalDialog | open, close, Escape, backdrop, title id, action slot |
PortalTree | expanded ids, selected id, disabled node, callback |
AssetAttachmentList | download URL, delete callback, formatSize, custom icon |
| 공통 | light/dark token, empty/error/loading 문구, public export |
이 단계의 완료 기준은 구현 파일이 변경되지 않은 상태에서 기준선 테스트가 통과하는 것이다. 테스트는 static markup만 사용하지 말고 가능한 상태 전이는 React Testing Library와 userEvent로 검증한다.
5.2 Phase 4.1 — dataShells 데이터 표시 전환
dataShells.tsx는 영향 범위가 크므로 상태 패널과 데이터 표시를 분리해 전환한다.
5.2.1 상태 패널
DataStatePanel을Empty,Result,Alert조합으로 전환한다.- 기존
role="status",role="alert"wrapper를 유지한다. - loading은
Skeleton또는Spin으로 표시하되,aria-busy와 상태 설명을 유지한다. - retry/dismiss/action callback은 기존 payload를 그대로 전달한다.
5.2.2 목록과 상세
DataListShell의 table을Table로 교체한다.- column id, header, align, width, cell renderer, row id를 보존한다.
- selected row와 row action callback의 호출 횟수를 보존한다.
DataDetailShell은Descriptions와Card를 조합한다.- null, undefined, 빈 문자열 표시 규칙을 기존과 동일하게 유지한다.
- status 값은
PortalStatusBadge로 통일한다.
5.2.3 pagination과 sorting
Phase 4.1에서는 기존 pagination payload를 우선 유지한다. Ant Design Table.pagination을 직접 public API로 노출하지 않는다. 내부 adapter가 current, pageSize, total, onChange를 기존 callback payload로 변환한다. 서버 cursor 기반 목록은 pagination component로 강제하지 않고 기존 load-more contract를 유지한다.
5.3 Phase 4.2 — contentComponents field·table·tabs 전환
첫 번째 하위 그룹은 side effect가 적은 primitive부터 전환한다.
| 기존 primitive | 내부 구현 | 보존 사항 |
|---|---|---|
PortalStack | Space 또는 wrapper | direction, gap, align, class |
PortalToolbar | Space와 flex wrapper | start/end slot, responsive class |
PortalPanel | Card | variant, padding, header, children |
PortalCard | Card | interactive click, title, footer |
PortalTextField | Input | value, defaultValue, onChange, error |
PortalTextArea | Input.TextArea | rows, disabled, error |
PortalSelect | Select | value, options, placeholder, change payload |
PortalTabs | Tabs | active key, default key, change callback |
PortalDataTable | Table | row key, columns, selection, empty state |
기존 .pf-field, .pf-table, .pf-tabs, .pf-panel class를 wrapper에 보존한다. Ant Design component에 직접 class를 부여할 수 없는 경우에는 wrapper를 추가하고 DOM landmark를 바꾸지 않는다.
5.4 Phase 4.3 — PortalDialog Modal 전환
PortalDialog는 접근성 위험이 높으므로 별도 작업으로 분리한다.
open=false에서는 아무것도 렌더링하지 않는 기존 계약을 유지한다.open=true에서 Ant DesignModal을 사용한다.- title과 description을
title,aria-describedby또는 custom header에 연결한다. onOpenChange(false)가 close button, Escape, backdrop 정책에 따라 호출된다.actionsslot은 Modal footer로 이동하되 callback과 순서를 보존한다.size를 Modal width로 변환한다.- 기존
.pf-dialog,.pf-dialog-overlay,.pf-dialog__bodyclass를 compatibility wrapper 또는 className으로 유지한다. - focus trap과 close 후 focus return을 테스트한다.
5.5 Phase 4.4 — PortalTree Tree 전환
PortalTree는 controlled/uncontrolled 양쪽을 지원해야 한다.
expandedIds를expandedKeys로 변환한다.defaultExpandedIds를defaultExpandedKeys로 변환한다.selectedId를selectedKeys로 변환한다.onExpandedChange는 Ant DesignonExpand의 key 목록을 string 배열로 변환한다.onSelect는 disabled node를 제외하고 원래 node 객체를 전달한다.- node icon, label, disabled, children 구조를 보존한다.
- connector와 density는 wrapper class로 유지하고 Ant Design Tree token은 provider에서 설정한다.
5.6 Phase 4.5 — AssetAttachmentList 첨부 목록 전환
첨부 목록은 List 기반으로 교체한다. 현재 public API에 upload callback이 없으므로 업로드 기능은 추가하지 않는다.
- asset name과 formatted size는
Typography.Text로 표시한다. - download action은
Button또는Typography.Link로 표시한다. target="_blank",rel="noreferrer",downloadLabel을 유지한다.- editable과
onDelete가 모두 있을 때만 danger Button을 표시한다. - empty list는
Empty와emptyLabel로 표시한다. - custom
renderIcon은 유지한다. - asset id를
Listkey로 유지한다. - FileStore 권한은 UI에서 재판정하지 않고 입력 props와 server contract를 따른다.
5.7 Phase 4.6 — PortalComponents와 공통 token 정리
Phase 4 마지막에는 이미 전환된 PortalStatusBadge, PortalEmptyState, PortalBreadcrumbs와 신규 data primitive의 token 사용을 비교한다.
- status tone이
Tagcolor와 동일한 정책을 사용하는지 확인한다. - Empty state의 title, description, action spacing을 통일한다.
- Breadcrumb의 separator와 link keyboard state를 확인한다.
Card,Table,Modal,Input,Select,Tree,List의 component token override를PortalConfigProvider로 이동한다.- 개별 CSS에 남은 Ant Design 내부 selector를 제거하거나 문서화한다.
6. 권장 커밋 순서
| 순서 | 권장 커밋 메시지 | 범위 |
|---|---|---|
| 1 | test: lock phase 4 data contracts | dataShells, contentComponents, attachments 기준선 |
| 2 | refactor: migrate data state shells to antd | state panel, feedback, data list/detail |
| 3 | refactor: migrate content fields and tables to antd | field, tabs, table, panel primitives |
| 4 | refactor: migrate portal dialog to antd modal | dialog와 focus contract |
| 5 | refactor: migrate portal tree to antd tree | tree conversion adapter |
| 6 | refactor: migrate asset attachments to antd list | attachment list and actions |
| 7 | test: add phase 4 accessibility coverage | axe, keyboard, focus, role tests |
| 8 | refactor: normalize phase 4 tokens and legacy wrappers | token and CSS cleanup |
각 커밋은 독립적으로 build와 test를 통과해야 한다. 하나의 커밋에서 dataShells와 contentComponents를 동시에 대규모로 교체하지 않는다.
7. 테스트 계획
7.1 Unit test matrix
| 대상 | 필수 테스트 |
|---|---|
dataShells | loading, empty, error, success, retry, dismiss, selected row, pagination, status tone |
PortalStack/Panel/Card | class, slot, variant, interactive callback |
| field controls | controlled, uncontrolled, disabled, error, value/change payload |
PortalTabs | active key, default key, tab change, disabled tab |
PortalDataTable | row key, columns, empty, selected row, action, sorting/pagination adapter |
PortalDialog | open, close, Escape, backdrop, title, description, action, focus return |
PortalTree | expansion, selection, disabled node, controlled/uncontrolled, nested children |
AssetAttachmentList | download, delete, custom icon, format size, empty, permission state |
| cross-cutting | theme, locale, SSR/static render, public exports, legacy class contract |
7.2 Accessibility matrix
- [ ] 모든 Table은 caption 또는 accessible label을 가진다.
- [ ] Table header와 cell 관계가 유지된다.
- [ ] loading Table은
aria-busy와 상태 설명을 제공한다. - [ ] Empty와 Result는 heading hierarchy를 깨뜨리지 않는다.
- [ ] Alert는 오류를
role="alert"로 전달한다. - [ ] status panel은
role="status"와 live region 정책을 유지한다. - [ ] Input, Select, TextArea는 visible label 또는 accessible label을 가진다.
- [ ] Select popup은 keyboard로 열고 선택할 수 있다.
- [ ] Tabs는
tablist,tab,tabpanel관계를 가진다. - [ ] Dialog는 accessible name, description, focus trap, Escape close를 가진다.
- [ ] Tree는
role="tree",treeitem, expanded/selected state를 가진다. - [ ] Attachment download와 delete action은 목적을 accessible name으로 전달한다.
- [ ] decorative icon은
aria-hidden="true"다. - [ ] focus outline이 design token에 의해 제거되지 않는다.
- [ ] axe 검사에서
button-name,color-contrast,dialog-name,form-field-multiple-labels,landmark-unique,nested-interactive,valid-aria-attribute오류가 없어야 한다.
8. 완료 기준
Phase 4는 다음 조건을 모두 만족할 때 완료로 판정한다.
| 기준 | 완료 조건 |
|---|---|
| 공개 API | 기존 export, props, callback 이름과 payload가 유지된다. |
| 데이터 상태 | loading, empty, error, success의 우선순위와 문구가 문서화된다. |
| Ant Design | 대상 primitive가 Ant Design 기반으로 렌더링된다. |
| 스타일 | legacy pf-* class와 design token ownership이 유지된다. |
| 접근성 | 자동 axe 검사와 keyboard/focus 수동 검증이 완료된다. |
| 호환성 | SSR/static rendering과 controlled/uncontrolled 양쪽이 동작한다. |
| 품질 | workspace build, ui-react check, 전체 Vitest가 통과한다. |
| 문서 | migration note, public prop, 상태 매트릭스, 예외가 기록된다. |
9. Phase 4에서 제외할 범위
directory-sync, filestore-adapter, editor-core, operations-core, runtime-core, security-core의 서버·도메인 계약은 수정하지 않는다. 새로운 업로드 API, 새로운 pagination public API, 새로운 form validation contract도 이번 Phase에서 추가하지 않는다. 이 범위는 API 설계와 소비 포털 migration이 필요하므로 별도 RFC 없이는 진행하지 않는다.