# 관리자 라우트 지연 로딩 구현 계획 | 문서 항목 | 내용 | |---|---| | 상태 | 구현·회귀 수정·검증 완료 | | 작성일 | 2026-08-06 | | 요구사항 기준 | [prd.md](./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-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와 `ProtectedAdminShell` route 판정 결과. - Produces: 동일 page props·render 조건, route별 dynamic import chunk와 `PageState` loading fallback. **TDD 절차:** - [x] **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로 실패하는지 확인한다. - [x] **GREEN: 최소 구현/통과 확인** — `protected-admin-shell.tsx`에서 React `lazy`·`Suspense`를 사용해 14개 보호 page의 named export를 동적 import하고 기존 page render 구간을 `PageState` fallback으로 감싼다. 같은 production graph test가 `exit 0`, `1/1`인지 확인한다. - [x] **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` 이상으로 통과하는지 확인한다. - [x] `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가 한 번 요청되는지 확인한다. - [x] 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 절차:** - [x] **RED: 실패 test 작성/실패 확인** — 320px·200% zoom에서 Korean leaf text가 음절 단위 세로 열로 렌더링되는지 `tests/e2e/character-workspace.spec.ts`에서 `Range.getClientRects()`로 검사한다. visual QA 스크린샷 `arl-lazy-routes-320-zoom200.png`에서 기존 문제가 확인됐다. - [x] **GREEN: 최소 구현/통과 확인** — `CharacterListItem` 텍스트에 `break-keep break-words`, `ResourcePagination` summary/label에 `break-keep`, mobile movement controls에 `grid-cols-1`과 `whitespace-nowrap`를 적용한다. - [x] **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, 한국어 음절 단위 세로 분리가 없다. - [x] 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`를 실행한다. - [x] 현재 상태 표의 완료 Task를 신규 회귀 Task까지 포함한 `3/3`으로 정정한다. - [x] `ARL-ISSUE-001`을 해결 상태로 정정한다. - [x] PRD에 review 링크를 연결하고 review 상태를 `수정 완료`로 갱신한다. - [x] 실제 검증 결과를 Progress에 누적한다. **검증 기준:** - **실행 명령:** `rg -n '3/3|ARL-ISSUE-001.*해결|phase1-admin-route-lazy-loading' docs/20260806_관리자라우트지연로딩`; `git diff --check`. - **기대 결과:** 세 현재 상태 marker와 review 링크가 확인되고 whitespace 오류가 없다. - **수동 확인:** 문서 표와 Task·Progress가 서로 같은 완료 상태를 표시한다. ### 완료 조건 - [x] `P1-T1`과 `P1-R1`의 체크박스와 완료 증거가 모두 충족됐다. - [x] `P1-R2`의 문서 정합성 복구와 검증 기록이 완료됐다. - [x] `ARL-001~008`이 구현 또는 Gate 증거로 추적된다. - [x] PRD 성공 기준과 현재 상태를 실제 결과로 갱신했다. - [x] 알려진 문서와 구현의 차이가 없다. ### 검증 방법 #### Phase 1 Gate **Goal 실행 `P1-GATE`:** route code splitting, 기능 보존과 공통 품질 기준을 최종 판정한다. - **시작 조건:** `P1-T1`과 `P1-R1` 완료. - **완료 증거:** 아래 자동·수동 검증 통과와 Progress 기록. - **범위 밖:** test 삭제·완화, warning limit 상향과 관련 없는 기능 수정. **실행 명령:** ```bash 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건. **수동 확인:** - [x] `/ai-characters` 직접 URL과 캐릭터 수정 내부 이동·뒤로 가기가 기존과 동일하다. - [x] Audio·Series·Community·FanTalk route 최초 진입에 loading 뒤 기존 화면이 표시된다. - [x] Network에서 현재 route 이외 page chunk가 초기 요청에 없고 최초 진입 후 cache된다. - [x] 1280px·320px·200% zoom에서 loading·page에 수평 overflow와 가려진 action이 없다. - [x] 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-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.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으로 바꾸고 기존 `PageState`를 `Suspense` 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 수동 확인 중 발견된 한국어 음절 단위 세로 분리 회귀를 `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개, 최대 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-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` 문서 현재 상태 정합성 복구 완료 | ## 최종 보고 형식 ```markdown 구현 결과: 보호된 관리자 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` — - Network·1280px·320px·200% zoom·keyboard·axe — <실제 결과> - 남은 항목: <없음 또는 구체적인 항목> - 문서: `docs/20260806_관리자라우트지연로딩/{prd.md,plan-task.md}` ``` 최종 보고는 실제 실행한 최신 검증 결과와 완료되지 않은 범위를 함께 기록한다.