Files

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 E2E 53 tests, typecheck, lint, production build가 통과했다.

범위

포함

  • 보호 page component의 React.lazy() 동적 import
  • 관리자 main의 Suspense·기존 PageState loading 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가 요구하는 default shape으로 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-R2P1-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와 ProtectedAdminShell route 판정 결과.
  • Produces: 동일 page props·render 조건, route별 dynamic import chunk와 PageState loading 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 bytes chunk로 실패하는지 확인한다.
  • GREEN: 최소 구현/통과 확인protected-admin-shell.tsx에서 React lazy·Suspense를 사용해 14개 보호 page의 named export를 동적 import하고 기존 page render 구간을 PageState fallback으로 감싼다. 같은 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개 이상이고 500kB warning이 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 graph 1/1, App focused 2 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, ResourcePagination summary/label에 break-keep, mobile movement controls에 grid-cols-1whitespace-nowrap를 적용한다.
  • REFACTOR: 정리/회귀 확인ResourcePagination unit 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 unit 4 tests 통과, Chromium E2E 53 tests 통과, mobile Chrome E2E 48 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-T1P1-R1의 체크박스와 완료 증거가 모두 충족됐다.
  • P1-R2의 문서 정합성 복구와 검증 기록이 완료됐다.
  • ARL-001~008이 구현 또는 Gate 증거로 추적된다.
  • PRD 성공 기준과 현재 상태를 실제 결과로 갱신했다.
  • 알려진 문서와 구현의 차이가 없다.

검증 방법

Phase 1 Gate

Goal 실행 P1-GATE: route code splitting, 기능 보존과 공통 품질 기준을 최종 판정한다.

  • 시작 조건: P1-T1P1-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건을 확인한다.

실행 순서와 의존성

  1. P1-T1에서 production graph 실패 test를 먼저 추가하고 최소 lazy import 구현과 focused 검증을 완료한다.
  2. P1-R1에서 320px·200% zoom CJK 회귀를 focused E2E와 공통 pagination unit으로 고정한다.
  3. P1-GATE에서 전체 unit·mock Chromium/mobile Chrome E2E와 수동 Network·접근성 검증을 완료한다.
  4. P1-R2에서 완료 문서 현재 상태를 fresh Gate 결과와 일치시키고 review를 종료한다.

P1-GATEP1-T1P1-R1 완료 전 시작하지 않는다. P1-R2P1-GATE 완료와 ARL-REV-P1-001 확정 뒤 시작한다.

변경 금지 항목

  • chunkSizeWarningLimitmanualChunks를 추가하지 않는다.
  • 기존 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.78kB production JS chunk와 500kB warning을 만든다.
  • 어떻게:
    • npm run build:prod — 성공, exit 0, 310 modules, JS 598.78kB, gzip 158.94kB, 500kB chunk 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-T1 production graph RED assertion 작성.

1차 구현 — 2026-08-06

  • 상태: 완료
  • 무엇을: ProtectedAdminShell의 14개 보호 page static import를 route-level React.lazy() named export mapping으로 바꾸고 기존 PageStateSuspense fallback으로 사용했다.
  • 왜: 초기 관리자 route에서 현재 page 외 보호 page 코드를 내려받지 않고, Vite 500kB chunk 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개, 최대 JS 315.09kB, 500kB warning 0건.
    • production preview 수동 확인 — /ai-characters 초기 요청에는 list 관련 chunk만 로드되고 detail·audio route 최초 진입 때 해당 page chunk가 추가 로드됨을 확인했다.
  • 남은 항목: P1-GATE와 visual QA 회귀 확인.

2차 수정 — 2026-08-06

  • 상태: 완료
  • 무엇을: 320px·200% zoom 수동 확인 중 발견된 한국어 음절 단위 세로 분리 회귀를 CharacterListItemResourcePagination의 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개, 최대 JS 315.09kB, gzip 93.77kB, 500kB warning 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-R11/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}`

최종 보고는 실제 실행한 최신 검증 결과와 완료되지 않은 범위를 함께 기록한다.