22 KiB
관리자 라우트 지연 로딩 구현 계획
| 문서 항목 | 내용 |
|---|---|
| 상태 | 구현·회귀 수정·검증 완료 |
| 작성일 | 2026-08-06 |
| 요구사항 기준 | prd.md |
| API 기준 | 변경 불필요 — 기존 인증·domain 계약 유지 |
| 현재 Phase | Phase 1. 보호 page code splitting 완료 |
| 현재 활성 Goal | 없음 |
목표
보호된 관리자 page를 route별로 지연 로드해 초기 JS chunk를 줄이면서 모든 기존 기능을 유지한다.
현재 상태
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|---|---|---|---|---|
| 1 | 완료 | 3/3 |
없음 | 없음 |
ProtectedAdminShell은 14개 보호 page component를React.lazy()동적 import로 로드한다.- Vite production build는 310 modules를 37개 JS chunk로 분리하고 최대 JS chunk는
315.09kB다. - 전체 unit
83 files / 462 tests, mock Chromium E2E53 tests, typecheck, lint, production build가 통과했다.
범위
포함
- 보호 page component의
React.lazy()동적 import - 관리자 main의
Suspense·기존PageStateloading fallback - production graph의 chunk 수·최대 크기 자동 검증
- 인증·route·domain 기능 unit와 mock Chromium E2E 회귀 검증
- 320px·200% zoom·keyboard·접근성 확인
제외
vite.config.ts의 warning limit·manual chunk 설정 변경- page default export 전환, route library와 새 helper·dependency 추가
- App shell·Login·AccessDenied page lazy loading
- API, 권한, page props, 상태 관리와 domain 기능 변경
- cropper만 별도로 lazy loading하는 추가 최적화
기술적 제약
- 기술 스택: React 19.2.8, TypeScript 6.0.3, Vite 8.1.5, Vitest 4.1.10, Playwright 1.61.1.
- 아키텍처:
ProtectedAdminShell의 기존 route 판정과 page 호출부를 유지하고 import boundary만 변경한다. - export: 기존 named export를 유지하며 각 lazy import에서 React가 요구하는
defaultshape으로 mapping한다. - fallback: 기존
PageState를 사용하고 shell·URL·focus 경계를 유지한다. - 성능 기준: 모든 production JS chunk
<=500,000 bytes, warning 0건. - 데이터·보안: API·token·mock production boundary를 변경하지 않는다.
- 의존성: 추가하지 않는다.
- 구현: RED → GREEN → REFACTOR 순서와 실제 결과를 Progress에 기록한다.
Phase 1. 보호 page code splitting
Phase 결과: 최초 관리자 route에는 현재 page 코드만 로드되고 다른 보호 page는 첫 진입 시 로드되며 기존 기능이 유지된다.
선행조건: ARL-001~008, ARL-DEC-001~003 확정.
Phase 완료 조건: P1-T1, P1-R1, P1-R2와 P1-GATE 완료, PRD 성공 기준과 Progress 갱신.
구현 항목
Task 1.1 보호 page route boundary 분리
Goal 실행 P1-T1: 보호 page 정적 import를 lazy import로 바꾸고 production chunk 경계를 자동 검증한다.
- 시작 조건:
prd.md가 구현 기준 확정 상태이고 활성 goal이 없음. - 완료 증거: RED·GREEN·REFACTOR 체크박스, production graph와 focused App test, production build 결과, Progress 기록.
- 범위 밖: route parser, page 내부 구현, API와 Vite manual chunk 설정.
Files:
- Create: 없음
- Modify:
src/app/protected-admin-shell.tsx - Modify:
src/app/App.test.tsx— lazy page heading 대기 보완 - Test:
src/shared/mocks/__tests__/production-graph.test.ts - Test:
src/app/App.protected-shell.test.tsx— assertion 보완이 필요할 때만 수정
Interfaces:
- Consumes: 14개 page module의 기존 named export와
ProtectedAdminShellroute 판정 결과. - Produces: 동일 page props·render 조건, route별 dynamic import chunk와
PageStateloading fallback.
TDD 절차:
- RED: 실패 test 작성/실패 확인 —
production-graph.test.ts의 기존 production build 결과에서 JS 파일이 2개 이상이고 모든 JS 파일이<=500,000 bytes인지 검사한다.npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts가 현재 단일598,785 byteschunk로 실패하는지 확인한다. - GREEN: 최소 구현/통과 확인 —
protected-admin-shell.tsx에서 Reactlazy·Suspense를 사용해 14개 보호 page의 named export를 동적 import하고 기존 page render 구간을PageStatefallback으로 감싼다. 같은 production graph test가exit 0,1/1인지 확인한다. - REFACTOR: 정리/회귀 확인 — 새 helper·barrel·config 없이 import와 fallback 위치만 정리한 뒤 production graph test와
npm run test:run -- src/app/App.protected-shell.test.tsx src/app/browser-location.test.ts가 각각1/1,2 files / 16 tests이상으로 통과하는지 확인한다. npm run build:prod결과에 JS chunk가 2개 이상이고500kBwarning이 0건인지 기록한다.
검증 기준:
-
실행 명령:
npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts;npm run test:run -- src/app/App.protected-shell.test.tsx src/app/browser-location.test.ts;npm run typecheck;npm run lint;npm run build:prod. -
기대 결과: 모든 명령
exit 0, production graph1/1, App focused2 files / 16 tests이상, type·lint 오류 0건, JS chunk 2개 이상, 최대 JS<=500,000 bytes, chunk warning 0건. -
수동 확인: production preview의 Network에서 첫 route 외 page chunk가 초기 요청에 없고 다른 보호 route 최초 진입에 해당 chunk가 한 번 요청되는지 확인한다.
-
TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.
Task 1.2 320px·200% zoom CJK 회귀 수정
Goal 실행 P1-R1: Phase Gate 수동 확인 중 발견된 한국어 음절 단위 세로 분리 회귀를 수정하고 공통 페이지네이션 모바일 배치 결정을 갱신한다.
- 시작 조건:
P1-T1구현 뒤 320px·200% zoom visual QA에서 CJK 음절 열 회귀가 확인됨. - 완료 증거: CJK E2E 회귀 test, ResourcePagination unit test, mock Chromium/mobile Chrome E2E, Decision Log와 Progress 기록.
- 범위 밖: 새 responsive component, pagination API 변경, page size options 변경, desktop/tablet 배치 변경.
Files:
- Create: 없음
- Modify:
src/features/characters/components/CharacterListItem.tsx - Modify:
src/shared/ui/resource-pagination.tsx - Test:
src/shared/ui/__tests__/resource-pagination.test.tsx - Test:
tests/e2e/character-workspace.spec.ts
Interfaces:
- Consumes:
ResourcePagination의 기존PageData,onPageChange,onSizeChange, accessible group/button labels. - Produces: 동일 pagination API와 desktop/tablet
sm:flex배치, mobile에서는 음절 단위 세로 분리를 막는 stacked movement controls.
TDD 절차:
- RED: 실패 test 작성/실패 확인 — 320px·200% zoom에서 Korean leaf text가 음절 단위 세로 열로 렌더링되는지
tests/e2e/character-workspace.spec.ts에서Range.getClientRects()로 검사한다. visual QA 스크린샷arl-lazy-routes-320-zoom200.png에서 기존 문제가 확인됐다. - GREEN: 최소 구현/통과 확인 —
CharacterListItem텍스트에break-keep break-words,ResourcePaginationsummary/label에break-keep, mobile movement controls에grid-cols-1과whitespace-nowrap를 적용한다. - REFACTOR: 정리/회귀 확인 —
ResourcePaginationunit expectation을 새 mobile 배치 계약으로 갱신하고 focused E2E와 axe 회귀를 통과시킨다.
검증 기준:
-
실행 명령:
npm run e2e:mock -- tests/e2e/character-workspace.spec.ts --project=chromium --grep "mobile zoom keeps Korean list and pagination text out of syllable columns";npm run test:run -- src/shared/ui/__tests__/resource-pagination.test.tsx;npm run e2e:mock:chromium;npm run e2e:mock:mobile-chrome. -
기대 결과: focused CJK E2E
1 passed, ResourcePagination unit4 tests통과, Chromium E2E53 tests통과, mobile Chrome E2E48 passed / 5 skipped. -
수동 확인: 320px·200% zoom에서 캐릭터 목록과 페이지네이션에 수평 overflow, 가려진 action, 한국어 음절 단위 세로 분리가 없다.
-
TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.
Task 1.3 완료 문서 현재 상태 정합성 복구
Goal 실행 P1-R2: ARL-REV-P1-001의 완료 Task 수와 해결된 원인 이슈 상태를 실제 구현·검증 결과에 맞춘다.
- 시작 조건:
P1-T1,P1-R1,P1-GATE완료와ARL-REV-P1-001확정. - 완료 증거: 현재 상태
3/3,ARL-ISSUE-001해결 상태, review 링크와 검증 기록. - 범위 밖: 애플리케이션 코드·test·API·기존 구현 결정 변경.
Files:
- Create:
docs/20260806_관리자라우트지연로딩/reviews/phase1-admin-route-lazy-loading.md - Modify:
docs/20260806_관리자라우트지연로딩/prd.md - Modify:
docs/20260806_관리자라우트지연로딩/plan-task.md - Test: 없음 — 현재 상태 문구만 정정하는 문서 Task다.
Interfaces:
- Consumes:
ARL-REV-P1-001,P1-T1,P1-R1,P1-GATE완료 증거. - Produces: 실제 완료 범위와 일치하는 PRD·계획·review 추적 상태.
TDD 예외 사유: 애플리케이션 동작을 변경하지 않는 문서 현재 상태 정정이라 실패 test를 추가하지 않는다.
대체 검증 방법: 완료 Task 수, 해결 이슈 상태와 review 링크를 rg로 확인하고 git diff --check를 실행한다.
- 현재 상태 표의 완료 Task를 신규 회귀 Task까지 포함한
3/3으로 정정한다. ARL-ISSUE-001을 해결 상태로 정정한다.- PRD에 review 링크를 연결하고 review 상태를
수정 완료로 갱신한다. - 실제 검증 결과를 Progress에 누적한다.
검증 기준:
- 실행 명령:
rg -n '3/3|ARL-ISSUE-001.*해결|phase1-admin-route-lazy-loading' docs/20260806_관리자라우트지연로딩;git diff --check. - 기대 결과: 세 현재 상태 marker와 review 링크가 확인되고 whitespace 오류가 없다.
- 수동 확인: 문서 표와 Task·Progress가 서로 같은 완료 상태를 표시한다.
완료 조건
P1-T1과P1-R1의 체크박스와 완료 증거가 모두 충족됐다.P1-R2의 문서 정합성 복구와 검증 기록이 완료됐다.ARL-001~008이 구현 또는 Gate 증거로 추적된다.- PRD 성공 기준과 현재 상태를 실제 결과로 갱신했다.
- 알려진 문서와 구현의 차이가 없다.
검증 방법
Phase 1 Gate
Goal 실행 P1-GATE: route code splitting, 기능 보존과 공통 품질 기준을 최종 판정한다.
- 시작 조건:
P1-T1과P1-R1완료. - 완료 증거: 아래 자동·수동 검증 통과와 Progress 기록.
- 범위 밖: test 삭제·완화, warning limit 상향과 관련 없는 기능 수정.
실행 명령:
npm run test:run
npm run e2e:mock:chromium
npm run e2e:mock:mobile-chrome
npm run typecheck
npm run lint
npm run build:prod
git diff --check
기대 결과: 모든 명령 exit 0, unit·mock Chromium/mobile Chrome E2E 실패 0건, type·lint·build 오류 0건, production JS chunk 2개 이상, 최대 JS <=500,000 bytes, chunk warning·whitespace 오류 0건.
수동 확인:
/ai-characters직접 URL과 캐릭터 수정 내부 이동·뒤로 가기가 기존과 동일하다.- Audio·Series·Community·FanTalk route 최초 진입에 loading 뒤 기존 화면이 표시된다.
- Network에서 현재 route 이외 page chunk가 초기 요청에 없고 최초 진입 후 cache된다.
- 1280px·320px·200% zoom에서 loading·page에 수평 overflow와 가려진 action이 없다.
- keyboard focus·skip link와 axe critical·serious 위반 0건을 확인한다.
실행 순서와 의존성
P1-T1에서 production graph 실패 test를 먼저 추가하고 최소 lazy import 구현과 focused 검증을 완료한다.P1-R1에서 320px·200% zoom CJK 회귀를 focused E2E와 공통 pagination unit으로 고정한다.P1-GATE에서 전체 unit·mock Chromium/mobile Chrome E2E와 수동 Network·접근성 검증을 완료한다.P1-R2에서 완료 문서 현재 상태를 fresh Gate 결과와 일치시키고 review를 종료한다.
P1-GATE는 P1-T1과 P1-R1 완료 전 시작하지 않는다.
P1-R2는 P1-GATE 완료와 ARL-REV-P1-001 확정 뒤 시작한다.
변경 금지 항목
chunkSizeWarningLimit과manualChunks를 추가하지 않는다.- 기존 named export, page props, route parser와 route path를 변경하지 않는다.
- page 내부 API·state·권한·UI를 함께 refactor하지 않는다.
- 새 dependency, lazy helper, barrel 또는 speculative prefetch를 추가하지 않는다.
- test를 삭제·skip·완화하거나 type 오류를 우회하지 않는다.
- 기존 Progress·Decision Log·review 기록을 삭제하거나 덮어쓰지 않는다.
의사결정 및 중단 규칙
- build에서 단일 chunk가 유지되면 warning limit을 올리지 말고 static import 잔존 여부를 확인한다.
- 공통 dependency chunk가
500,000 bytes를 넘으면 근거를 기록하고 사용자와 별도 최적화 범위를 결정한다. - lazy 전환으로 기존 test가 timing 차이만 드러내면 사용자 결과 assertion은 유지하고 비동기 대기만 최소 보완한다.
- 기능 assertion이 실패하면 lazy 변경을 완료로 처리하지 않고 원인을 수정한다.
- 범위가 바뀌면 PRD Decision Log와 이 계획을 먼저 갱신한다.
- 같은 차단 사유가 3회 연속 반복되고 독립 작업도 불가능할 때만 goal을
blocked로 갱신한다.
Progress
기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다.
계획 작성 — 2026-08-06
- 상태: 완료
- 무엇을: route-level
React.lazy()선택을ARL-001~008, 단일 구현 Task와 Phase Gate로 정규화했다. - 왜: 현재 14개 보호 page의 static import가 단일
598.78kBproduction JS chunk와500kBwarning을 만든다. - 어떻게:
npm run build:prod— 성공, exit 0, 310 modules, JS598.78kB, gzip158.94kB,500kBchunk warning 1건.npm run test:run -- src/app/App.protected-shell.test.tsx src/app/browser-location.test.ts— 성공, exit 0,2 files / 16 tests.- code·test 변경과 수동 Network 검증 — 미실행, 구현 요청 범위가 아님.
- 남은 항목:
P1-T1,P1-GATE. - 다음 행동:
P1-T1production graph RED assertion 작성.
1차 구현 — 2026-08-06
- 상태: 완료
- 무엇을:
ProtectedAdminShell의 14개 보호 page static import를 route-levelReact.lazy()named export mapping으로 바꾸고 기존PageState를Suspensefallback으로 사용했다. - 왜: 초기 관리자 route에서 현재 page 외 보호 page 코드를 내려받지 않고, Vite
500kBchunk warning을 warning limit 상향 없이 제거하기 위해서다. - 어떻게:
npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts— RED 성공, 기존 단일 JS chunk 때문에expected 1 to be greater than or equal to 2로 실패 확인.npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts— GREEN 성공, exit 0,1 file / 1 test.npm run test:run -- src/app/App.protected-shell.test.tsx src/app/browser-location.test.ts— REFACTOR 회귀 성공, exit 0,2 files / 16 tests.npm run build:prod— 성공, exit 0, JS chunk 37개, 최대 JS315.09kB,500kBwarning 0건.- production preview 수동 확인 —
/ai-characters초기 요청에는 list 관련 chunk만 로드되고 detail·audio route 최초 진입 때 해당 page chunk가 추가 로드됨을 확인했다.
- 남은 항목:
P1-GATE와 visual QA 회귀 확인.
2차 수정 — 2026-08-06
- 상태: 완료
- 무엇을: 320px·200% zoom 수동 확인 중 발견된 한국어 음절 단위 세로 분리 회귀를
CharacterListItem과ResourcePagination의 wrapping 규칙으로 수정하고 E2E 회귀 test를 추가했다. - 왜: lazy route 자체의 기능 문제는 아니지만 Phase Gate의 320px·200% zoom 수동 확인 기준을 만족하지 못했다.
- 어떻게:
npm run e2e:mock -- tests/e2e/character-workspace.spec.ts --project=chromium --grep "mobile zoom keeps Korean list and pagination text out of syllable columns"— 성공, exit 0,1 passed.npm run test:run -- src/shared/ui/__tests__/resource-pagination.test.tsx— 성공, exit 0,1 file / 4 tests.npm run e2e:mock -- tests/e2e/character-workspace.spec.ts --project=chromium --grep "has no critical or serious axe violations on the list"— 첫 실행은Port 8889 is already in use환경 문제로 실패, 포트 해제 확인 후 재실행 성공, exit 0,1 passed.
- 남은 항목: fresh Phase Gate 전체 검증.
Phase 1 Gate — 2026-08-06
- 상태: 완료
- 무엇을: route code splitting, 기능 보존, 접근성·반응형 회귀와 공통 품질 기준을 최종 검증했다.
- 왜:
P1-T1완료 뒤ARL-001~008과 Phase 완료 조건을 실제 실행 결과로 판정하기 위해서다. - 어떻게:
npm run test:run— 성공, exit 0,83 files / 462 tests.npm run e2e:mock:chromium— 성공, exit 0,53 tests.npm run e2e:mock:mobile-chrome— 성공, exit 0,48 passed / 5 skipped.npm run typecheck— 성공, exit 0.npm run lint— 성공, exit 0.npm run build:prod— 성공, exit 0, 310 modules, JS chunk 37개, 최대 JS315.09kB, gzip93.77kB,500kBwarning 0건.git diff --check— 성공, exit 0, whitespace 오류 0건.- 수동 production preview —
/ai-characters직접 URL, detail/audio route 진입, 뒤로 가기, Network chunk lazy loading, 1280px·320px·200% zoom 수평 overflow 없음, console error 0건을 확인했다.
- 남은 항목: 없음.
회귀 감사 — 2026-08-06
- 상태: 확정
- 무엇을: 구현·test·build와 계획의 현재 상태를 다시 대조해
ARL-REV-P1-001을 확정하고P1-R2로 전환했다. - 왜: 완료된
P1-T1·P1-R1이1/1로 표시되고 해결된ARL-ISSUE-001이확정으로 남아 있었다. - 어떻게:
npm run test:run— 성공, exit 0,83 files / 462 tests.npm run typecheck— 성공, exit 0.npm run lint— 성공, exit 0.npm run build:prod— 성공, exit 0, 310 modules, JS 37개, 최대315.09kB, chunk warning 0건.npm run e2e:mock:chromium— 최초 sandbox port 권한으로 실행 불가, 권한 허용 후 성공, exit 0,53 passed.npm run e2e:mock:mobile-chrome— 성공, exit 0,48 passed / 5 skipped.
- 남은 항목:
P1-R2문서 현재 상태 정정과 review 종료.
P1-R2 문서 정합성 회귀 수정 — 2026-08-06
- 상태: 완료
- 무엇을: 완료 Task 수를 신규 회귀 Task까지 포함한
3/3으로 갱신하고ARL-ISSUE-001을 해결 상태로 바꿨으며 PRD에 Phase 1 review를 연결했다. - 왜: 완료 구현과 계획의 현재 상태가 달라 후속 작업자가 남은 범위를 잘못 판단할 수 있었다.
- 어떻게:
rg -n '3/3|ARL-ISSUE-001.*해결|phase1-admin-route-lazy-loading' docs/20260806_관리자라우트지연로딩— 성공, 세 현재 상태 marker와 PRD·plan·review 연결 확인.git diff --check— 성공, exit 0, whitespace 오류 0건.
- 남은 항목: 없음.
Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|---|---|---|---|---|---|
| 2026-08-06 | ARL-PLAN-DEC-001 |
확정 | 보호 page를 route-level React.lazy()로 분리한다. |
실제 초기 loading 비용과 chunk warning을 함께 줄인다. | P1-T1, P1-GATE, prd.md |
| 2026-08-06 | ARL-PLAN-DEC-002 |
확정 | 기존 production graph test에 chunk 수·크기 assertion을 추가한다. | 이미 Vite production build와 임시 directory 정리를 검증하는 가장 가까운 test다. | P1-T1 |
| 2026-08-06 | ARL-PLAN-DEC-003 |
확정 | 기존 PageState를 Suspense fallback으로 사용한다. |
새 component 없이 디자인·접근성 관례를 유지한다. | P1-T1 |
| 2026-08-06 | ARL-PLAN-DEC-004 |
확정 | ResourcePagination의 mobile movement controls는 동일 폭 2열 대신 1열 stacked 배치로 대체한다. |
320px·200% zoom에서 한국어 버튼 텍스트가 음절 단위 세로 열로 분리되는 회귀를 막고 touch target과 label 가독성을 유지한다. Desktop/tablet은 기존 sm:flex 배치를 유지한다. |
P1-R1, ARL-006, ARL-007 |
| 2026-08-06 | ARL-PLAN-DEC-005 |
확정 | 완료 문서의 stale Task 수와 이슈 상태를 P1-R2에서 현재 구현 결과와 맞춘다. |
ARL-REV-P1-001의 문서 정합성 회귀 판정. |
P1-R2, Phase 1 review |
발견된 문제
| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 |
|---|---|---|---|---|---|
ARL-ISSUE-001 |
Medium | 해결 | 보호 page 정적 import로 production JS가 598.78kB 단일 chunk이며 Vite 경고가 반복된다. |
P1-T1 |
route-level lazy import와 build boundary test 완료 |
ARL-ISSUE-002 |
Medium | 해결 | 320px·200% zoom에서 캐릭터 목록과 공통 페이지네이션 한국어 텍스트가 음절 단위 세로 열로 분리됐다. | P1-R1, P1-GATE |
break-keep·stacked mobile pagination과 CJK E2E 회귀 test |
ARL-ISSUE-003 |
Low | 해결 | 완료 Task 수와 해결된 원인 이슈 상태가 구현 전 값으로 남아 있다. | P1-R2 |
ARL-REV-P1-001 문서 현재 상태 정합성 복구 완료 |
최종 보고 형식
구현 결과: 보호된 관리자 page가 route별 chunk로 분리되고 기존 기능을 유지한다.
- 변경: `ProtectedAdminShell` page import boundary와 production graph assertion
- 결정: `ARL-DEC-001` — route-level `React.lazy()`
- 검증:
- `npm run test:run` — <실제 결과>
- `npm run e2e:mock:chromium` — <실제 결과>
- `npm run build:prod` — <chunk 수·최대 크기·warning 수>
- Network·1280px·320px·200% zoom·keyboard·axe — <실제 결과>
- 남은 항목: <없음 또는 구체적인 항목>
- 문서: `docs/20260806_관리자라우트지연로딩/{prd.md,plan-task.md}`
최종 보고는 실제 실행한 최신 검증 결과와 완료되지 않은 범위를 함께 기록한다.