feat(ai-character): Mock Preview 모드 구현

This commit is contained in:
Yu Sung
2026-07-27 23:23:36 +09:00
parent b4841ff579
commit 2a5efb0f3e
38 changed files with 4763 additions and 289 deletions

View File

@@ -10,7 +10,7 @@
| 문서 항목 | 내용 |
|---|---|
| 상태 | Phase 0~1 완료, 신규 Phase 2 Mock Preview 착수 전 |
| 상태 | Phase 0~2 완료, Phase 3 Character workspace 착수 전 |
| 최초 작성일 | 2026-07-25 |
| 재작성일 | 2026-07-26 |
| 요구사항 기준 | [prd.md](./prd.md) |
@@ -277,7 +277,7 @@ npm run build:prod
**Goal 실행 `P0-R1`:** Phase 0 테스트 격리와 루트 셸·Playwright 증거 정합성을 복구하고 회귀를 방지한다.
- 연결 리뷰: `REV-P0-001`, `REV-P0-002`, `REV-P0-003`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P0-001`, `REV-P0-002`, `REV-P0-003`
- 시작 조건: review finding을 보존하고 기존 완료 체크를 되돌리지 않는다.
- 완료 증거: stubbed global 복원 회귀 test, root `main`과 대표 content assertion, 현재 Playwright mode URL 전략과 실행 가능한 agent-guide 문서 경로 정정 기록, P0-GATE 통과.
- 범위 밖: Phase 1 인증 동작 전면 변경, test framework 교체.
@@ -485,7 +485,7 @@ npm run build
**Goal 실행 `P1-R2`:** image 10MB 표현을 백엔드와 동일한 exact byte 계약으로 확정한다.
- 연결 리뷰: `REV-P1-010`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P1-010`
- 상태: 완료.
- 결정: 백엔드 exact byte를 `10,485,760 bytes`로 확정했다. `10,485,760 bytes`는 허용하고 `10,485,761 bytes`부터 거부한다.
- 완료 증거: PRD, API 계약, `IMAGE_MAX_BYTES`, `limit-1/limit/limit+1` 경계 테스트를 같은 값으로 정렬했다.
@@ -494,7 +494,7 @@ npm run build
**Goal 실행 `P1-R3`:** Phase 1 인증의 보호 경계, 오류 피드백, logout 단일 요청 계약을 복구한다.
- 연결 리뷰: `REV-P1-002`, `REV-P1-006`, `REV-P1-007`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P1-002`, `REV-P1-006`, `REV-P1-007`
- 완료 증거: stale ADMIN probe pending·403 동안 보호 shell 비노출, probe `size=20`, 서버 한국어 message 우선 표시, logout in-flight 중 API 1회 호출.
- 범위 밖: 인증 방식 또는 token 저장 방식 교체, Phase 3 권한 기능.
@@ -506,7 +506,7 @@ npm run build
**Goal 실행 `P1-R4`:** Phase 1 파일·미디어 입력의 crop 결과와 audio 형식 조합 계약을 복구한다.
- 연결 리뷰: `REV-P1-003`, `REV-P1-004`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P1-003`, `REV-P1-004`
- 완료 증거: source crop rectangle 기반 출력, 1200×600 → 1:1 no-upscale 600×600과 상·하단 alpha 유지 test, 자유 비율, pointer drag, 확장자↔MIME 조합 table test.
- 범위 밖: 외부 crop library, 서버 파일 변환 구현.
@@ -520,7 +520,7 @@ npm run build
**Goal 실행 `P1-R5`:** Phase 1 공유 UI의 modal focus, 필드 오류 상태, keyboard event 경계를 복구한다.
- 연결 리뷰: `REV-P1-005`, `REV-P1-008`, `REV-P1-009`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P1-005`, `REV-P1-008`, `REV-P1-009`
- 완료 증거: 세 modal의 focus trap과 trigger focus return, FileField `aria-invalid`, audio descendant control Enter·Space 격리.
- 범위 밖: 공유 UI 전체 교체, 시각 디자인 재작업.
@@ -566,7 +566,7 @@ npm run build
**Goal 실행 `P1-R1`:** Phase 1 완료 판정을 재현 가능한 산출물·통합·Gate 증거로 복구한다.
- 연결 리뷰: `REV-P1-001`
- 연결 리뷰: [Phase 0·1 리뷰](./reviews/review-phase-0-1.md) — `REV-P1-001`
- 상태: 완료.
- 완료한 부분: P1-GATE 명령은 `src/app/App.test.tsx`를 포함하도록 보정했고, P1-R2/R3/R4/R5의 자동·E2E 검증 기록을 누적했다.
- 결정: `PageHeader`는 구현·소비가 없고 현재 필요한 반복 소비 컴포넌트가 아니므로 Phase 1 shared 산출물에서 제거한다. 필요하다고 판단되는 도메인 Phase에서 추가한다.
@@ -582,20 +582,23 @@ npm run build
- **시작 조건:** `P1-GATE` 완료.
- **완료 조건:** `P2-T1`~`P2-T3`, `P2-GATE` 완료. mock/server 분리와 production 차단이 자동 test와 E2E로 고정됨.
- **현재 상태:** Phase 1 완료 후 새로 추가된 독립 후속 Phase. Phase 0·1의 완료 이력은 변경하지 않는다.
- **현재 상태:** 완료. `P2-T1`~`P2-T3`, `P2-GATE`, `P2-R1`~`P2-R16`의 구현·수정·검증이 끝났으며 Phase 3 진행 가능.
**요구사항:** `MOCK-001~009`, PRD `11.1~11.2`, `12`, `13`.
**주요 Files:**
- Modify: `package.json`, `.env.example`, `src/shared/config/env.ts`, `src/shared/config/env.test.ts`
- Modify: `src/main.tsx`, `src/app/providers.tsx`
- Create: `src/shared/mocks/{browser,handlers,fixtures,store}.ts`
- Create: `src/shared/mocks/__tests__/{mode-boundary,handlers,store}.test.ts`
- Modify: `src/app/App.tsx`, `src/main.tsx`, `vite.config.ts`, `playwright.config.ts`
- Modify: `src/app/App.test.tsx`, `src/app/admin-pages.tsx`, `src/app/browser-location.ts`
- Create: `src/app/protected-admin-shell.tsx`
- Create: `src/shared/mocks/{browser,handlers,contract}.ts`
- Create: `src/shared/mocks/__tests__/{mode-boundary,auth-handlers,mock-preview-docs,production-graph}.test.ts`
- Create: `src/shared/mocks/browser.test.ts`
- Create: `src/shared/ui/mock-mode-banner.tsx`
- Create: `src/shared/ui/__tests__/mock-mode-banner.test.tsx`
- Create: `public/mockServiceWorker.js`
- Create: `tests/e2e/mock-preview-shell.spec.ts`
- Create: `tests/e2e/{mock-mode-boundary,mock-preview-shell,server-mode-boundary}.spec.ts`
- Modify when implementation is complete: `README.md`, `docs/agent-guide/{environment,scripts}.md`
### Task 2.1 explicit API mode·production 경계
@@ -606,13 +609,27 @@ npm run build
- **완료 증거:** env/script/bootstrap focused test, `dev` server 기본값, `dev:mock` explicit mode, production mock 거부와 404 no-fallback test.
- **범위 밖:** Character 이후 도메인 fixture와 실제 backend endpoint 구현.
- [ ] `VITE_API_MODE=server | mock`을 검증하고 누락 기본값은 `server`로 고정하는 실패 test를 작성한다.
- [ ] `npm run dev:mock`만 Vite mock mode와 browser worker를 시작하고 기본 `npm run dev`는 실제 `VITE_API_BASE_URL`을 사용하는 script test를 작성한다.
- [ ] Playwright가 같은 spec을 explicit mode로 실행할 수 있도록 `e2e:mock`과 server mode 명령을 정의하고 각 webServer 환경을 test한다.
- [ ] `import.meta.env.DEV && apiMode === "mock"`일 때만 browser module을 dynamic import하고 worker 준비 후 React를 mount한다.
- [ ] production mode에서 `mock`을 설정하면 build/start 전에 설명 가능한 오류로 거부하고 worker·fixture가 production graph에서 실행되지 않는 test를 작성한다.
- [ ] `server` mode의 404·network error가 browser mock을 시작하거나 응답을 교체하지 않는 test를 작성한다.
- [ ] worker의 unhandled request 정책은 error로 두어 누락된 handler가 실제 backend로 조용히 통과하지 않게 한다.
- [x] `VITE_API_MODE=server | mock`을 검증하고 누락 기본값은 `server`로 고정하는 실패 test를 작성한다.
- [x] `npm run dev:mock`만 Vite mock mode와 browser worker를 시작하고 기본 `npm run dev`는 실제 `VITE_API_BASE_URL`을 사용하는 script test를 작성한다.
- [x] Playwright가 같은 spec을 explicit mode로 실행할 수 있도록 `e2e:mock`과 server mode 명령을 정의하고 각 webServer 환경을 test한다.
- [x] `import.meta.env.DEV && apiMode === "mock"`일 때만 browser module을 dynamic import하고 worker 준비 후 React를 mount한다.
- [x] production mode에서 `mock`을 설정하면 build/start 전에 설명 가능한 오류로 거부하고 worker·fixture가 production graph에서 실행되지 않는 test를 작성한다.
- [x] `server` mode의 404·network error가 browser mock을 시작하거나 응답을 교체하지 않는 test를 작성한다.
- [x] worker의 unhandled request 정책은 error로 두어 누락된 handler가 실제 backend로 조용히 통과하지 않게 한다.
**P2-T1 구현 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/config/env.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts`는 mode/default/script 미구현으로 2 files 중 5 tests가 기대대로 실패했고, `VITE_API_MODE=mock npm run e2e -- tests/e2e/mock-mode-boundary.spec.ts`는 worker 미등록과 unhandled 정책 미구현으로 8 tests가 실패했다.
- GREEN focused: `npm run test:run -- src/shared/config/env.test.ts src/shared/mocks/browser.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/api/__tests__/client.test.ts`는 4 files / 18 tests 통과, `npm run e2e:mock -- tests/e2e/mock-mode-boundary.spec.ts`는 4 browser projects 통과, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts`는 4 browser projects 통과했다.
- Production boundary: `VITE_API_MODE=mock npm run build:prod`는 guard 추가 전에는 부당하게 성공했고, guard 추가 후 `VITE_API_MODE=mock is only available during development` 오류로 기대대로 거부됐다.
- Quality: `npm run typecheck`, `npm run lint`, `npm run test:run`(31 files / 117 tests), `npm run build:dev`, `npm run build:prod`, `git diff --check`를 성공했다. Playwright로 `http://127.0.0.1:8888/login``http://127.0.0.1:8889/login`을 직접 열어 server mode는 worker registration 0건, mock mode는 `/mockServiceWorker.js` controller 등록을 확인했고, QA용 dev server 포트 `8888`·`8889`가 비었음을 확인했다.
**P2-T1 리뷰 보강 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts`는 production build 산출물에 `mockServiceWorker.js`가 복사되어 실패했고, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts`는 404·network error 후 no-fallback 증거가 없어 8 tests가 실패했다.
- GREEN: production mode에서 `publicDir` 복사를 끄고 production graph test를 실제 `NODE_ENV=production` build 조건으로 맞춘 뒤 같은 focused unit은 2 files / 3 tests 통과했다. server mode E2E는 404·network error 요청 후에도 browser MSW registration 0건임을 확인해 4 browser projects / 12 tests 통과했다.
- Re-review 보강: production graph test가 output file path까지 검사하도록 보강했고, server 404·network error는 오류 alert 표시 후 worker registration 0건을 확인하도록 `App`의 route error 표시 조건을 수정했다. `npm run test:run -- src/shared/mocks/__tests__/production-graph.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts src/app/App.test.tsx`는 3 files / 16 tests 통과했고, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts`는 4 browser projects / 12 tests 통과했다.
- Guide sync: `VITE_API_MODE`, `dev:mock`, `e2e:mock`, production mock 거부 기준을 `docs/agent-guide/environment.md``docs/agent-guide/scripts.md`에 반영했다.
### Task 2.2 공통 preview session·fixture store
@@ -622,12 +639,18 @@ npm run build
- **완료 증거:** auth handler/store/banner focused test와 login → protected shell mock preview E2E 기록.
- **범위 밖:** 도메인별 endpoint handler와 계약 미제공 fixture.
- [ ] fixture는 정규화 `ApiResponse<T>`와 실제 endpoint·header·body contract를 사용하고 별도 mock DTO를 만들지 않는다.
- [ ] ADMIN login·logout, 401·403 fixture와 새로고침 시 seed로 초기화되는 in-memory store를 만든다.
- [ ] mock mutation은 같은 store의 목록·상세 응답을 갱신하고 browser storage에 domain fixture를 영구 저장하지 않는다.
- [ ] mock mode임을 지속적으로 표시하는 접근 가능한 banner와 server mode에서 banner가 없는 test를 작성한다.
- [ ] JWT·password·signed URL·multipart body가 mock logger와 storage에 기록되지 않는 test를 작성한다.
- [ ] handler와 fixture가 `src/shared/test/server.ts`의 Node test lifecycle을 변경하지 않고 필요한 contract factory만 공유하게 한다.
- [x] fixture는 정규화 `ApiResponse<T>`와 실제 endpoint·header·body contract를 사용하고 별도 mock DTO를 만들지 않는다.
- [x] ADMIN login·logout, 401·403 fixture와 새로고침 시 seed로 초기화되는 in-memory store를 만든다.
- [x] mock mutation은 같은 store의 목록·상세 응답을 갱신하고 browser storage에 domain fixture를 영구 저장하지 않는다.
- [x] mock mode임을 지속적으로 표시하는 접근 가능한 banner와 server mode에서 banner가 없는 test를 작성한다.
- [x] JWT·password·signed URL·multipart body가 mock logger와 storage에 기록되지 않는 test를 작성한다.
- [x] handler와 fixture가 `src/shared/test/server.ts`의 Node test lifecycle을 변경하지 않고 필요한 contract factory만 공유하게 한다.
**P2-T2 실행 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/ui/__tests__/mock-mode-banner.test.tsx src/app/App.test.tsx src/shared/mocks/browser.test.ts``@/shared/mocks/handlers`·`@/shared/ui/mock-mode-banner` 미구현, App mock banner 부재, browser worker handler 미등록 기대 실패로 4 files 중 5 failures가 발생했다. `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts --project=chromium`은 mock login handler 부재로 `/login`에 머물러 기대대로 실패했다.
- GREEN: `createMockHandlers(createMockStore())``POST /admin/member/login`, `POST /member/logout`, `GET /api/v2/admin/ai-characters?page=0&size=20`을 정규화 envelope로 처리하고 logout mutation 후 같은 store에서 token을 폐기하며 새 store 생성 시 seed로 초기화한다. 리뷰 보강으로 logout 후 재login 시 같은 store에서 ADMIN token이 다시 활성화되도록 고정했다. 같은 focused unit 명령은 4 files / 25 tests 통과했고, `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts`는 4 browser projects / 8 tests 통과했다.
- Sensitive data/storage: handler는 logger를 추가하지 않았고, focused test에서 login password/JWT 요청 후 `localStorage`, mock `sessionStorage`, cookie, IndexedDB, console log/warn/error에 fixture domain data와 민감값을 기록하지 않음을 확인했다. `src/shared/test/server.ts` lifecycle은 변경하지 않았다.
### Task 2.3 Mock Preview 반응형·문서화
@@ -637,10 +660,15 @@ npm run build
- **완료 증거:** 320px·desktop mock shell E2E, axe 결과, README와 environment/scripts 가이드의 실제 명령 동기화 기록.
- **범위 밖:** 도메인별 최종 UI와 server integration 완료 주장.
- [ ] mock banner가 320px·200% zoom에서 핵심 control을 가리지 않고 axe critical·serious 위반이 없는지 E2E로 확인한다.
- [ ] README에 `npm run dev``npm run dev:mock`, mode 차이, mock data reset, production 금지와 no-auto-fallback을 기록한다.
- [ ] 구현이 완료된 뒤 `docs/agent-guide/environment.md``scripts.md`에 실제 환경 변수와 명령을 추가한다.
- [ ] 후속 도메인 Phase가 handler·fixture·mock E2E를 소유한다는 규칙을 문서화한다.
- [x] mock banner가 320px·200% zoom에서 핵심 control을 가리지 않고 axe critical·serious 위반이 없는지 E2E로 확인한다.
- [x] README에 `npm run dev``npm run dev:mock`, mode 차이, mock data reset, production 금지와 no-auto-fallback을 기록한다.
- [x] 구현이 완료된 뒤 `docs/agent-guide/environment.md``scripts.md`에 실제 환경 변수와 명령을 추가한다.
- [x] 후속 도메인 Phase가 handler·fixture·mock E2E를 소유한다는 규칙을 문서화한다.
**P2-T3 실행 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts`는 README에 실제 `dev`/`dev:mock`/`e2e`/`e2e:mock` script, mode 차이, mock data reset, production 금지, no-auto-fallback 기록이 없고 `docs/agent-guide/environment.md``scripts.md`에 reset·domain handler/fixture/mock E2E 소유 규칙이 없어 2 tests가 기대대로 실패했다. `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts --project=chromium`은 새 320px·200% zoom 및 axe 확인을 포함해 5 tests가 통과해 기존 mock shell UI는 문서 보강 전에도 요구 접근성 동작을 만족함을 확인했다.
- GREEN: README에 `npm run dev`/`dev:mock`/`e2e`/`e2e:mock`의 실제 명령, server mode와 mock mode 차이, mock data reset, production mock 거부, no-auto-fallback, 후속 도메인 handler·fixture·mock E2E 소유 규칙을 추가했다. `docs/agent-guide/environment.md`에는 `VITE_API_MODE=server | mock`, reset, production 금지, no-auto-fallback을 동기화하고 `docs/agent-guide/scripts.md`에는 실제 script와 domain ownership rule을 동기화했다. 같은 focused unit 명령은 2 files / 4 tests 통과했고, `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts --project=chromium`은 5 tests 통과했다. 최종 확인으로 `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts`는 4 browser projects / 20 tests 통과, `npm run typecheck`, `npm run lint`, `git diff --check`도 통과했다.
### Phase 2 Gate
@@ -661,6 +689,644 @@ npm run build:prod
**Expected:** `npm run dev:mock`에서는 ADMIN login → protected shell과 mock banner가 동작하고 실제 backend 요청은 0건이다. 기본 server mode는 실제 API 오류를 그대로 처리하며 production build는 browser mock을 활성화하지 않는다.
**P2-GATE 실행 기록 (2026-07-27):**
- `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 7 files / 20 tests 통과.
- `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 4 browser projects / 20 tests 통과. mock login → protected shell, logout 후 재login, 320px·200% zoom banner/control, axe critical·serious 0을 확인했다.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0.
- LSP diagnostics: `src/shared/mocks/__tests__/mock-preview-docs.test.ts` clean, `tests/e2e` directory 6 files / 0 diagnostics. 단일 `mock-preview-shell.spec.ts` LSP 호출은 3초 fresh diagnostics timeout이었으나 directory diagnostics와 typecheck/lint로 보완했다.
- 판정: Phase 2 mock/server mode 경계, auth preview, production build 경계, mock preview 반응형·접근성·문서화 완료. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### Task R2.1 — 보호 route 검증 실패 시 fail-closed 복구
**Goal 실행 `P2-R1`:** Phase 2 no-auto-fallback 오류 UI가 Phase 1의 보호 shell 비노출 경계를 우회하지 않게 하고 회귀를 방지한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-001`
- 시작 조건:
- 저장된 ADMIN session으로 `/ai-characters`에 진입한 뒤 probe가 404 또는 network error로 실패할 때 보호 shell이 노출되는 현재 동작을 실패 test로 재현한다.
- 완료 증거:
- probe 성공 전과 401·403·404·network error에서 보호 shell 비노출
- 404·network error는 mock fallback 없이 보호 shell 밖의 오류 UI로 표시
- focused App test, server mode boundary E2E, auth E2E와 P2-GATE 실행 기록
- 범위 밖:
- 인증 방식, session 저장 방식 또는 API client 전면 교체
- Phase 3 Character 화면 구현
- [x] 404·network error와 이전 오류 뒤 새 session probe pending에서 보호 shell이 노출되는 실패 test를 추가한다.
- [x] 성공한 현재 token probe만 보호 shell을 열고 실패 오류는 shell 밖에서 표시하는 최소 상태 전이를 구현한다.
- [x] `src/app/App.test.tsx`, `tests/e2e/server-mode-boundary.spec.ts`, 기존 auth E2E와 P2-GATE를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R1 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts` — 404·network error에서 보호 shell `banner/main`이 렌더되어 App test 2건이 기대대로 실패했다.
- GREEN focused: `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/browser.test.ts` — 3 files / 27 tests 통과.
- Surface: `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts` — 4 browser projects / 12 tests 통과. 404·network error 후 mock worker 0건, 보호 shell `main` 0건, logout button 0건을 확인했다.
- Regression: `npm run e2e -- tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts` — 4 browser projects / 16 tests 통과.
- Reviewer blocker 보강: 같은 token 재사용 logout → login 뒤 probe 실패 시 이전 검증 token이 남는 회귀를 추가로 확인했다. RED `npm run test:run -- src/app/App.test.tsx` — 1 test failed. GREEN 같은 command — 1 file / 19 tests 통과. 보강 후 `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/browser.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 4 files / 31 tests 통과, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts` — 12 tests 통과, `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 20 tests 통과.
#### create_goal objective 초안 — P2-R1
- objective: P2-R1의 확정 review 항목 REV-P2-001을 수정하고 Phase 1 보호 route 경계의 회귀를 방지한다. plan-task.md에 추가된 Task R2.1만 수행한다.
- 완료 조건: 404·network error를 포함한 probe 비성공 상태에서 보호 shell 비노출, 독립 오류 UI, focused test, server/auth E2E, P2-GATE와 누적 기록이 모두 확인된다.
- 금지 조건: 인증·session 아키텍처 교체나 Phase 3 Character 구현을 포함하지 않는다.
- 중단 조건: 보호 route 확인 endpoint 자체가 변경됐다는 외부 계약이 확인되면 계약 문서를 먼저 갱신한다.
### Task R2.2 — Mock Preview API origin·JWT 오류 계약 복구
**Goal 실행 `P2-R2`:** browser mock handler를 설정된 API base URL에만 결합하고 invalid·revoked JWT logout을 계약 status로 반환한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-002`, `REV-P2-003`
- 시작 조건:
- 잘못된 origin의 login이 200을 반환하고 invalid·revoked JWT logout이 200을 반환하는 현재 동작을 각각 실패 test로 재현한다.
- 완료 증거:
- 설정된 `VITE_API_BASE_URL`의 정확한 URL·method만 handler가 처리하고 다른 origin은 `onUnhandledRequest: "error"` 경계에 남음
- invalid·revoked JWT logout은 API Contract §1.2의 401 오류 envelope를 반환
- 정상 login → logout → login 흐름과 403 fixture 회귀 없음
- focused handler test, mock preview E2E와 P2-GATE 실행 기록
- 범위 밖:
- 계약 미제공 endpoint·DTO·회원 role 정책 추정
- 도메인별 fixture 선행 구현
- [x] wrong-origin login과 invalid·두 번째 logout의 현재 200 응답을 실패 test로 고정한다.
- [x] handler 생성 시 API base URL을 주입해 production endpoint URL에만 매칭한다.
- [x] token access 상태를 확인한 뒤 logout store를 변경하고 invalid·revoked token에 401을 반환한다.
- [x] auth handler focused test, mock preview E2E와 P2-GATE를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R2 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts` — wrong-origin login이 200, invalid JWT logout이 200으로 응답해 auth handler test 2건이 기대대로 실패했다.
- GREEN focused: `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/browser.test.ts` — 3 files / 27 tests 통과. `createMockHandlers(store, apiBaseUrl)` exact origin, invalid·revoked JWT 401, browser worker runtime env 주입을 확인했다.
- Surface: `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 4 browser projects / 20 tests 통과. 정상 mock login → protected shell, logout 후 재login, 320px·200% zoom, axe critical·serious 0건을 확인했다.
- P2 focused Gate: `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 7 files / 23 tests 통과.
#### create_goal objective 초안 — P2-R2
- objective: P2-R2의 확정 review 항목 REV-P2-002와 REV-P2-003을 수정하고 mock/production API 계약 경계를 일치시킨다. plan-task.md에 추가된 Task R2.2만 수행한다.
- 완료 조건: exact API origin, invalid·revoked JWT 401, 기존 정상 auth preview, focused test, mock E2E, P2-GATE와 누적 기록이 모두 확인된다.
- 금지 조건: 계약 미제공 endpoint·DTO·회원 role 의미를 추정하거나 도메인 fixture를 추가하지 않는다.
- 중단 조건: logout의 non-ADMIN token 정책이 현재 계약만으로 판정되지 않으면 해당 경로는 확장하지 않고 백엔드 확인 항목으로 기록한다.
### Task R2.3 — Phase 번호 표시·구현 문서 정합성 복구
**Goal 실행 `P2-R3`:** Phase 2 완료 뒤 남은 사용자 표시와 plan의 Files·Progress를 실제 구현에 맞게 정렬한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-004`, `REV-P2-005`
- 시작 조건:
- 보호 shell 빈 상태가 Character 연결 시점을 Phase 2로 표시하고, Phase 2 주요 Files와 하단 Progress가 실제 변경 경로·완료 기록과 다른 상태를 확인한다.
- 완료 증거:
- 빈 상태 문구와 관련 test가 Character 구현 Phase 3을 가리킴
- Phase 2 주요 Files가 실제 수정·생성·test 경로와 일치함
- 하단 검증 기록에 Phase 2 구현의 무엇을/왜/실제 명령·결과/남은 항목이 누적됨
- 문서 test, App focused test와 `git diff --check` 실행 기록
- 범위 밖:
- Phase 3 Character 기능 구현
- 기존 Phase 2 구현·Gate 기록 삭제 또는 덮어쓰기
- [x] 현재 Phase 2 문구와 Files·Progress 불일치를 test와 파일 존재 검사로 고정한다.
- [x] 사용자 표시를 Phase 3으로 바꾸고 Phase 2 주요 Files를 실제 경로로 갱신한다.
- [x] Phase 2 구현·Gate 검증을 하단 Progress에 기존 기록을 보존한 채 누적한다.
- [x] 문서/App focused test와 `git diff --check`를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R3 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/mock-preview-docs.test.ts` — Phase 3 placeholder와 실제 Phase 2 Files·Progress 기록이 없어 2 files 중 3 tests가 기대대로 실패했다.
- GREEN focused: 같은 command — 2 files / 21 tests 통과.
- Final Gate: 1차 `npm run test:run` — 34 files / 135 tests 통과. Reviewer blocker 보강 후 최종 `npm run test:run` — 34 files / 136 tests 통과. `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod`, `git diff --check` 모두 성공.
- Production guard: `VITE_API_MODE=mock npm run build:prod` — 기대대로 `VITE_API_MODE=mock is only available during development` 오류로 거부됐다.
- LSP diagnostics: `src/app` directory 3 files / 0 diagnostics, `src/shared/mocks` directory 8 files / 0 diagnostics. `App.tsx` 단일 fresh diagnostics는 timeout이었고 directory diagnostics와 typecheck로 보완 확인했다.
#### create_goal objective 초안 — P2-R3
- objective: P2-R3의 확정 review 항목 REV-P2-004와 REV-P2-005를 수정하고 Phase 번호 표시와 계획 문서 추적성을 복구한다. plan-task.md에 추가된 Task R2.3만 수행한다.
- 완료 조건: 사용자 문구, 실제 Files, 하단 Progress, focused test와 문서 검증 기록이 서로 일치한다.
- 금지 조건: Phase 3 기능 구현이나 기존 완료 기록 삭제·덮어쓰기를 수행하지 않는다.
- 중단 조건: Phase 번호가 다시 변경되면 현재 기록을 지우지 않고 새 결정 기록을 먼저 추가한다.
### Task R2.4 — 동일 세션 재진입 fail-closed 복구
**Goal 실행 `P2-R4`:** 최초 보호 route 검증 성공 뒤 같은 session으로 route를 이탈·재진입해도 새 probe 성공 전에는 보호 shell을 다시 열지 않는다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-006`
- 시작 조건:
- 같은 `AuthSessionRecord``/ai-characters` 성공 → `/login` 이탈 → `/ai-characters` 재진입 뒤 probe 실패 시 보호 shell이 노출되는 현재 동작을 실패 test로 재현한다.
- 완료 증거:
- 최초 성공 뒤 동일 session 재진입 pending·404·network·403에서 보호 shell 비노출
- focused App test, server boundary E2E와 P2 Gate 실행 기록
- 범위 밖:
- 인증/session 저장 방식 전면 교체
- Phase 3 Character 화면 구현
- [x] 같은 session route 이탈·재진입 뒤 pending·404 실패에서 보호 shell이 노출되는 실패 test를 추가한다.
- [x] route 이탈 또는 새 보호 route probe 시작 시 이전 검증 상태가 새 진입을 열지 못하게 최소 상태 전이를 구현한다.
- [x] focused App test와 server boundary E2E를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R4 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/app/App.test.tsx` — 동일 session `/login` 이탈 후 `/ai-characters` 재진입 404에서 보호 `main`이 남아 1 test가 기대대로 실패했다.
- GREEN focused: 같은 command — 1 file / 20 tests 통과. route visit key로 이전 보호 route 검증을 새 진입에 재사용하지 않게 했다.
- Refactor guard: `ProtectedAdminShell``src/app/protected-admin-shell.tsx`로 분리해 `App.tsx`를 153 pure LOC로 낮췄고, App focused test와 lint가 통과했다.
### Task R2.5 — Auth fixture status·media type 계약 복구
**Goal 실행 `P2-R5`:** mock auth fixture가 API Contract의 비ADMIN status와 login media type 경계를 완화하지 않게 한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-007`
- 시작 조건:
- 비ADMIN logout 401과 `text/plain` login 200을 실패 test로 재현한다.
- 완료 증거:
- 비ADMIN logout 403, invalid·revoked token logout 401 유지
- login은 `application/json`만 허용하고 지원하지 않는 media type은 415 오류 envelope 반환
- 정상 login/logout 회귀 없음
- 범위 밖:
- 제공되지 않은 login credential 정책과 회원 role 의미 확장
- [x] 비ADMIN logout 403과 non-JSON login 415 실패 test를 추가한다.
- [x] logout token access 분기와 login media type 검증을 계약 status에 맞춘다.
- [x] auth handler focused test와 mock auth preview 회귀를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R5 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/mocks/__tests__/auth-handlers.test.ts` — 비ADMIN logout이 401, `text/plain` login이 200으로 응답해 2 tests가 기대대로 실패했다.
- GREEN focused: 같은 command — 1 file / 10 tests 통과. 비ADMIN logout은 403, invalid·revoked logout은 401 유지, unsupported media type login은 415로 고정했다.
### Task R2.6 — 모든 mock route state의 지속 안내 복구
**Goal 실행 `P2-R6`:** mock mode의 login·성공 shell·403·보호 오류 상태에서 Mock Preview 안내를 지속 표시한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-008`
- 시작 조건:
- mock mode 403과 404/network 보호 오류 화면에서 banner가 사라지는 현재 동작을 실패 test로 재현한다.
- 완료 증거:
- mock mode login·성공 shell·403·404/network에서 banner 표시
- server mode에서는 banner 미표시 유지
- 접근성·320px 회귀 없음
- 범위 밖:
- 오류 화면 디자인 개편
- [x] 403과 보호 오류 화면의 mock banner 실패 test를 추가한다.
- [x] Mock Preview 안내를 모든 mock route state의 공통 상위 경계에 한 번만 배치한다.
- [x] focused App/banner test와 mock preview E2E를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R6 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/app/App.test.tsx` — mock mode access denied와 보호 route 오류 화면에서 `Mock Preview` status가 없어 2 tests가 기대대로 실패했다.
- GREEN focused: 같은 command — 1 file / 22 tests 통과. login·성공 shell·403·보호 오류가 공통 `RouteFrame`의 banner를 사용한다.
### Task R2.7 — Mode별 bare E2E 실행 경계 복구
**Goal 실행 `P2-R7`:** bare `npm run e2e``npm run e2e:mock`이 각 mode에 유효한 spec만 수집·실행하게 한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-009`
- 시작 조건:
- 두 bare E2E 명령의 `--list`가 같은 spec 집합을 수집하고 mock bare 실행이 실패하는 현재 동작을 실패 test로 재현한다.
- 완료 증거:
- bare `npm run e2e``npm run e2e:mock`이 각 mode 유효 spec만 실행해 exit 0
- 목록·실행 contract test와 scripts 문서가 일치
- 범위 밖:
- Playwright config 복제와 CI 전면 재구성
- [x] package script 또는 Playwright 설정의 mode별 spec 경계 실패 test를 추가한다.
- [x] 가장 작은 script/config 변경으로 server 전용·mock 전용 spec 수집을 분리한다.
- [x] `--list`, bare E2E 실행과 docs sync test를 실행한다.
- [x] 결과를 plan-task.md 검증 기록과 [Phase 2 리뷰 문서](./reviews/review-phase-2.md)에 누적한다.
**P2-R7 수정 검증 기록 (2026-07-27):**
- RED: `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts` — bare `e2e` script가 mode별 spec 경계를 명시하지 않아 1 test가 기대대로 실패했다.
- GREEN focused: `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 2 files / 5 tests 통과.
- 목록 검증: `npm run e2e -- --list` — server 4 files / 32 tests, `npm run e2e:mock -- --list` — mock 2 files / 24 tests로 분리됐다.
- Surface: `npm run e2e` — 32 tests 통과, `npm run e2e:mock` — 24 tests 통과.
**P2-R4~P2-R7 통합 검증 기록 (2026-07-27):**
- `npm run test:run -- src/app/App.test.tsx src/shared/mocks/__tests__/auth-handlers.test.ts src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 4 files / 37 tests 통과.
- `npm run test:run` — 34 files / 141 tests 통과.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0.
- `VITE_API_MODE=mock npm run build:prod` — 기대대로 `VITE_API_MODE=mock is only available during development`로 exit 1.
- `test ! -e dist/mockServiceWorker.js`, `git diff --check HEAD` — 모두 exit 0.
- LSP diagnostics: `src/app` directory 4 files / 0 diagnostics, `src/shared/mocks` directory 8 files / 0 diagnostics.
### Task R2.8 — Phase 2 완료 문서 추적성 복구
**Goal 실행 `P2-R8`:** `P2-R4`~`P2-R7` 이후의 실제 변경 경로·검증 범위와 Phase 2 plan/review 메타데이터를 일치시킨다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-010`
- 시작 조건:
- 3차 review 반영 전 `git diff HEAD --name-only`은 37개 경로인데 review 상단과 종료 판정은 36개로 기록된 상태였음을 확인한다.
- Phase 2 `주요 Files``src/app/App.test.tsx`, `src/app/admin-pages.tsx`, `src/app/browser-location.ts`, `src/app/protected-admin-shell.tsx`를 포함하지 않는 상태를 확인한다.
- 완료 증거:
- Phase 2 `주요 Files``P2-R4`~`P2-R7`까지의 실제 코드·test 경로와 일치함
- review 대상·working tree 경로 수·종료 판정과 3차 재검증 기록이 현재 범위와 일치함
- 완료된 회귀 Task의 Files·Interfaces와 검증 근거가 독립 실행자가 추측하지 않을 수준으로 기록됨
- 문서 대체 검증과 `git diff --check` 결과가 plan/review에 누적됨
- 범위 밖:
- 애플리케이션 코드·test·설정 변경
- 기존 완료 체크와 과거 검증 기록 삭제 또는 덮어쓰기
**Files:**
- Modify: `docs/20260725_AI캐릭터관리자웹/plan-task.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- Test: 없음 — 이 Task는 완료 문서의 소유 경로와 검증 메타데이터만 정정한다.
**Interfaces:**
- Consumes: `git diff HEAD --name-only`, `P2-R4`~`P2-R7` 구현·검증 기록, `REV-P2-010`
- Produces: Phase 3 실행자와 reviewer가 사용할 최신 Phase 2 Files·review 범위·검증 기준
**TDD 예외 사유:** 실행 동작을 변경하지 않는 문서 정합성 수정이므로 실패 test를 추가하면 제품 동작과 무관한 문자열 고정 test만 늘어난다.
**대체 검증 방법:** 현재 변경 경로와 Phase 2 Files·review 범위를 직접 대조하고, review ID·goal 연결과 Markdown diff를 명령으로 검사한다.
- [x] Phase 2 `주요 Files``P2-R4`~`P2-R7`의 실제 코드·test 경로를 추가한다.
- [x] review 상단의 대상·working tree 기준과 종료 판정을 3차 재검증 범위에 맞춘다.
- [x] 완료된 회귀 Task의 Files·Interfaces·검증 근거 누락을 기존 기록을 보존한 채 보완한다.
- [x] 아래 대체 검증을 실행하고 실제 결과를 plan/review에 누적한다.
**P2-R8 수정 검증 기록 (2026-07-27):**
- 대체 RED: `git diff HEAD --name-only`로 기존 tracked 변경 37개 경로를 확인했고, `rg -n 'P2-R(10|[1-9])|REV-P2-0(0[1-9]|1[0-3])|주요 Files|기준 commit 또는 working tree' docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`에서 `REV-P2-010``P2-R8`이 문서 추적성 미충족 상태로 남아 있음을 확인했다.
- GREEN: Phase 2 `주요 Files``src/app/App.test.tsx`, `src/app/admin-pages.tsx`, `src/app/browser-location.ts`, `src/app/protected-admin-shell.tsx`를 추가하고, review 상단·요약·종료 판정·수정 후 검증 기록을 `P2-R8` 완료와 `P2-R9`~`P2-R10` 잔여 상태로 정렬했다.
- 검증: `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
**검증 기준:**
- **실행 명령:** `git diff HEAD --name-only`, `rg -n 'P2-R(10|[1-9])|REV-P2-0(0[1-9]|1[0-3])|주요 Files|기준 commit 또는 working tree' docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`, `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- **기대 결과:** 실제 변경 경로와 문서의 소유 Files·review 범위 차이 0건, review ID와 goal 연결 누락 0건, whitespace 오류 0건.
- **수동 확인:** review 상단·발견 요약·plan 전환·종료 판정·수정 후 검증 기록이 모두 `P2-R1`~`P2-R10`의 현재 상태를 같은 의미로 표시한다.
### Task R2.9 — Mock Preview 모바일 메뉴의 반응형·inert 경계 복구
**Goal 실행 `P2-R9`:** Mock Preview 모바일 메뉴가 열린 상태에서도 banner를 배경 inert 경계에 포함하고 desktop breakpoint 전환 시 숨은 overlay와 inert 상태를 함께 해제한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-011`, `REV-P2-012`
- 시작 조건:
- 320px에서 모바일 메뉴를 열면 Mock Preview banner의 `closest('[inert]')``null`인 현재 동작을 browser 재현으로 확인한다.
- 같은 상태에서 viewport를 1,200px로 넓히면 overlay는 `display:none`이지만 `main`의 inert ancestor와 `aria-hidden=true`가 남는 현재 동작을 browser 재현으로 확인한다.
- 완료 증거:
- 모바일 메뉴가 열린 동안 banner와 shell 본문이 같은 background inert·`aria-hidden` 경계에 포함됨
- viewport가 `lg` 이상으로 바뀌면 mobile menu state와 inert·`aria-hidden`이 해제되고 desktop navigation·logout·main을 keyboard와 pointer로 사용할 수 있음
- breakpoint 자동 종료 시 숨겨진 mobile trigger로 focus를 복귀하지 않음
- App focused test, mock responsive E2E, 기존 accessibility shell E2E와 P2 Gate 실행 기록
- 범위 밖:
- Admin shell navigation 구조 전면 교체
- Phase 3 Character UI와 새로운 breakpoint 체계 도입
**Files:**
- Modify: `src/app/App.tsx`
- Modify: `src/app/protected-admin-shell.tsx`
- Test: `src/app/App.test.tsx`
- Test: `tests/e2e/mock-preview-shell.spec.ts`
**Interfaces:**
- Consumes: `MockModeBanner({ apiMode }: { readonly apiMode: ApiMode })`, `ProtectedAdminShell({ routeError }: { readonly routeError: string | null })`, Tailwind `lg=1024px` shell breakpoint
- Produces: `ProtectedAdminShell({ routeError, apiMode }: { readonly routeError: string | null; readonly apiMode: ApiMode })`와 mobile overlay 표시 여부·background `inert`·`aria-hidden`이 항상 같은 상태인 composition
**TDD 절차:**
- [x] **RED: 실패 테스트 작성/실패 확인**`src/app/App.test.tsx`에 mock banner가 열린 menu의 inert background에 포함되는 test와 `lg` 전환 시 menu state가 닫히는 test를 추가하고, `npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx`가 두 assertion에서 실패하는지 확인한다.
- [x] **GREEN: 최소 구현/통과 확인** — banner와 shell을 하나의 background inert 경계로 조합하고 native viewport change에서 mobile state만 닫는 최소 구현으로 같은 명령을 통과시킨다.
- [x] **REFACTOR: 정리/회귀 확인** — focus 복귀 조건과 breakpoint 이름을 정리한 뒤 App focused test, mock preview E2E, 기존 accessibility shell E2E와 P2 focused Gate를 실행한다.
- [x] TDD 단계와 아래 검증 기준의 실제 결과를 plan/review에 누적한다.
**검증 기준:**
- **실행 명령:** `npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx`, `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx`, `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts`, `npm run e2e -- tests/e2e/accessibility-shell.spec.ts`, `npm run typecheck`, `npm run lint`
- **기대 결과:** focused unit 2 files / 26 tests 이상, P2 focused 7 files / 25 tests 이상, mock preview 4 projects / 24 tests 이상, accessibility shell 4 projects / 12 tests, type·lint 오류 0건; 1,024px·1,200px 전환 뒤 hidden mobile overlay와 inert background 잔존 0건.
- **수동 확인:** 320px에서 메뉴를 열면 banner와 본문이 보조기기 탐색에서 제외되고 menu만 탐색 가능하며, 열린 상태로 1,024px와 1,200px로 넓히면 desktop navigation·logout·main이 즉시 다시 동작한다.
### Task R2.10 — 보호 route 오류의 fail-closed 재시도 복구
**Goal 실행 `P2-R10`:** 보호 route probe의 404·network 오류 화면에서 보호 shell을 열지 않은 채 사용자가 명시적으로 재시도할 수 있게 한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-013`
- 시작 조건:
- `P2-R9` 완료.
- 저장된 ADMIN session의 probe가 404 또는 network error로 실패하면 오류 alert만 있고 재시도·이동 control이 없는 현재 동작을 확인한다.
- 완료 증거:
- 404·network error 화면에 keyboard로 사용할 수 있는 명시적 재시도 control 제공
- 재시도 중과 재실패 상태에서 보호 shell·navigation·logout 비노출 유지
- 재시도 probe가 성공한 뒤에만 현재 session·route visit의 보호 shell 표시
- server가 제공한 정상 오류 envelope의 한국어 message는 보존하고 network·invalid response에는 안전한 공통 안내 사용
- App focused test, server boundary E2E와 P2 Gate 실행 기록
- 범위 밖:
- 자동 retry·자동 mock fallback
- Phase 3 Character 목록 오류 UI와 API client 전면 교체
**Files:**
- Modify: `src/app/App.tsx`
- Test: `src/app/App.test.tsx`
- Test: `tests/e2e/server-mode-boundary.spec.ts`
**Interfaces:**
- Consumes: `ProtectedRouteErrorPage({ message }: { readonly message: string })`, `BrowserLocationSnapshot.visitKey`, `ApiError.message`
- Produces: `ProtectedRouteErrorPage({ message, onRetry }: { readonly message: string; readonly onRetry: () => void })`와 수동 retry마다 새 probe attempt를 식별하는 App state
**TDD 절차:**
- [x] **RED: 실패 테스트 작성/실패 확인**`src/app/App.test.tsx`에 404 → retry 성공과 network → retry 실패의 fail-closed test를 추가하고 `npm run test:run -- src/app/App.test.tsx`가 retry control 부재로 두 test에서 실패하는지 확인한다.
- [x] **GREEN: 최소 구현/통과 확인** — 오류 page에 retry button과 현재 session·route에 귀속된 새 probe attempt만 추가해 같은 명령을 통과시킨다.
- [x] **REFACTOR: 정리/회귀 확인** — 오류 message·attempt state 이름을 정리한 뒤 App focused test, server boundary E2E, P2 focused Gate와 전체 unit을 실행한다.
- [x] TDD 단계와 아래 검증 기준의 실제 결과를 plan/review에 누적한다.
**검증 기준:**
- **실행 명령:** `npm run test:run -- src/app/App.test.tsx`, `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx`, `npm run test:run`, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts`, `npm run typecheck`, `npm run lint`
- **기대 결과:** App 1 file / 26 tests 이상, P2 focused 7 files / 25 tests 이상, 전체 34 files / 145 tests 이상, server boundary 4 projects / 16 tests 이상, type·lint 오류 0건; retry 성공 전 보호 `main`·logout button 0건.
- **수동 확인:** 404와 offline 상태에서 retry button의 accessible name·focus indicator를 확인하고, 실패 중 shell이 보이지 않으며 연결 복구 후 한 번의 수동 retry로 shell이 열린다.
### Task R2.11 — Phase 2 working tree 전체 경로 집계 복구
**Goal 실행 `P2-R11`:** tracked diff와 untracked 파일을 함께 집계해 Phase 2 review 범위와 완료 문서가 실제 working tree 전체를 누락 없이 표시하게 한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-014`
- 시작 조건:
- `P2-R8` 완료.
- `git diff HEAD --name-only | wc -l`은 37을 반환하지만 `git status --porcelain=v1 | wc -l`은 38을 반환하고 `git ls-files --others --exclude-standard``src/app/protected-admin-shell.tsx`가 존재함을 확인한다.
- 완료 증거:
- review의 현재 working tree 범위를 tracked 37개와 untracked 1개를 포함한 38개 변경 항목으로 기록
- `P2-R8` 대체 검증과 후속 reviewer 명령이 untracked 파일을 빠뜨리지 않는 `git status --short --untracked-files=all` 기준을 사용
- plan/review의 현재 범위·종료 판정·검증 기록과 실제 working tree 항목 차이 0건
- 범위 밖:
- 애플리케이션 코드·test·설정 변경
- 과거 시점의 37개 tracked diff 실행 결과 삭제 또는 덮어쓰기
**Files:**
- Modify: `docs/20260725_AI캐릭터관리자웹/plan-task.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- Test: 없음 — working tree 집계 명령과 완료 문서 메타데이터만 정정한다.
**Interfaces:**
- Consumes: `git diff HEAD --name-only`, `git status --short --untracked-files=all`, `git ls-files --others --exclude-standard`, `REV-P2-014`
- Produces: tracked·untracked를 모두 포함하는 Phase 2 working tree 범위와 reviewer 검증 기준
**TDD 예외 사유:** 실행 동작을 변경하지 않는 문서·검증 명령 정정이므로 제품 test가 아닌 실제 Git 상태 대조가 실패·성공 증거다.
**대체 검증 방법:** tracked diff 수, 전체 status 항목 수와 untracked 목록을 각각 수집해 합계와 문서 범위를 대조한다.
- [x] `P2-R8`의 대체 검증 기준에 `git status --short --untracked-files=all`과 untracked 확인 명령을 추가한다.
- [x] review 상단·4차 검증·종료 판정의 현재 working tree 범위를 tracked 37개 + untracked 1개 = 38개 항목으로 정렬한다.
- [x] 과거 37개 tracked diff 기록은 당시 실행 결과로 보존하고 현재 전체 범위와 구분한다.
- [x] 아래 대체 검증과 문서 diff 검사를 실행하고 결과를 plan/review에 누적한다.
**검증 기준:**
- **실행 명령:** `git diff HEAD --name-only | wc -l`, `git status --short --untracked-files=all | wc -l`, `git ls-files --others --exclude-standard`, `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- **기대 결과:** 현재 기준 tracked diff 37개, untracked `src/app/protected-admin-shell.tsx` 1개, working tree 전체 38개 변경 항목, 현재 문서 범위 누락과 whitespace 오류 0건.
- **수동 확인:** review 상단·4차 검증·발견 요약·plan 전환·종료 판정이 `P2-R1`~`P2-R13`의 현재 상태와 38개 working tree 항목을 같은 의미로 표시한다.
### Task R2.12 — 보호 route probe 대기 상태의 접근 가능한 피드백 복구
**Goal 실행 `P2-R12`:** 최초 보호 route 확인과 수동 retry가 진행되는 동안 보호 shell을 숨긴 채 사용자와 보조기기에 명시적인 loading 상태를 제공한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-015`
- 시작 조건:
- `P2-R10` 완료.
- retry 응답을 350ms 지연하면 `#root` child 0개, `main`·`status`·`alert` 0개이고 active element가 `BODY`인 현재 Chromium 동작을 확인한다.
- 완료 증거:
- 최초 probe와 retry pending 모두 `RouteFrame` 안에 지속적으로 보이는 한국어 loading 상태와 `role="status"` 제공
- pending 동안 보호 `main`·navigation·logout 비노출과 mock mode banner 지속 표시 유지
- 성공·404·network·401·403의 기존 fail-closed 분기와 수동 retry 동작 회귀 없음
- App focused test, 지연된 server boundary E2E, P2 focused Gate와 전체 unit 실행 기록
- 범위 밖:
- 보호 shell skeleton 선노출
- 자동 retry·자동 mock fallback과 전역 router 도입
**Files:**
- Modify: `src/app/App.tsx`
- Test: `src/app/App.test.tsx`
- Test: `tests/e2e/server-mode-boundary.spec.ts`
**Interfaces:**
- Consumes: `PageState({ state: "loading", title, description })`, `RouteFrame({ apiMode, children })`, `ProtectedRouteVerification`
- Produces: 현재 session·route visit·retry attempt가 미검증인 동안 렌더되는 `RouteFrame` + accessible loading `PageState`
**TDD 절차:**
- [x] **RED: 실패 테스트 작성/실패 확인**`src/app/App.test.tsx`에 최초 probe pending 전용 test를 추가하고 기존 404 retry test에 retry pending assertion을 보강해 두 pending 시나리오가 `role="status"`를 제공하면서 보호 shell을 숨기는지 확인한다. `npm run test:run -- src/app/App.test.tsx`가 status 부재로 실패하는지 확인한다.
- [x] **GREEN: 최소 구현/통과 확인** — 기존 `PageState` loading variant를 미검증 branch에 조합하는 최소 구현으로 같은 명령을 통과시킨다.
- [x] **REFACTOR: 정리/회귀 확인** — loading copy와 branch 이름을 정리하고 기존 404 retry E2E에 350ms pending status assertion을 추가한 뒤 focused·전체 회귀를 실행한다.
- [x] TDD 단계와 아래 검증 기준의 실제 결과를 plan/review에 누적한다.
**검증 기준:**
- **실행 명령:** `npm run test:run -- src/app/App.test.tsx`, `VITE_API_MODE=server npx playwright test tests/e2e/server-mode-boundary.spec.ts`, `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx`, `npm run test:run`, `npm run typecheck`, `npm run lint`
- **기대 결과:** App 1 file / 27 tests, server boundary 4 projects / 12 tests 이상, P2 focused 7 files / 25 tests 이상, 전체 34 files / 147 tests 이상, type·lint 오류 0건; 최초 pending 전용 test 1건과 기존 retry test 보강으로 두 pending 시나리오에서 visible `role="status"` 1건과 보호 `main`·logout 0건.
- **수동 확인:** 375px server mode에서 최초 진입과 retry 응답을 각각 350ms 이상 지연해 한국어 loading 안내가 보이고 빈 root가 발생하지 않으며, 성공 뒤에만 shell이 열린다.
### Task R2.13 — Mode별 focused E2E 필터와 network retry 증거 복구
**Goal 실행 `P2-R13`:** bare mode 분리를 유지하면서 CLI spec 필터가 실제로 focused 실행되게 하고 network retry 실패를 server boundary E2E로 고정한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-016`
- 시작 조건:
- `P2-R12` 완료.
- `npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts`가 4 files / 32 tests를 수집하지만 직접 `VITE_API_MODE=server npx playwright test tests/e2e/server-mode-boundary.spec.ts --list`는 1 file / 12 tests만 수집함을 확인한다.
- `P2-R10` 검증 기준은 server boundary 16 tests 이상을 요구하지만 현재 spec은 3 scenarios / 12 tests이고 network error test는 retry를 실행하지 않음을 확인한다.
- 완료 증거:
- bare `npm run e2e`는 server 4개 spec만, bare `npm run e2e:mock`은 mock 2개 spec만 실행
- 두 npm script 뒤에 전달한 file filter가 고정 spec 목록과 합쳐지지 않고 해당 file만 수집
- server boundary에 network error → 수동 retry → network 재실패의 retry 유지·shell 비노출·worker 0건 E2E 추가
- mode boundary contract, focused list/실행, bare mode E2E, P2 Gate와 문서 동기화 기록
- 범위 밖:
- Playwright config 복제
- project matrix·worker 수·CI 전체 구조 변경
**Files:**
- Modify: `package.json`
- Modify: `playwright.config.ts`
- Modify: `README.md`
- Modify: `docs/agent-guide/scripts.md`
- Test: `src/shared/mocks/__tests__/mode-boundary.test.ts`
- Test: `src/shared/mocks/__tests__/mock-preview-docs.test.ts`
- Test: `tests/e2e/server-mode-boundary.spec.ts`
**Interfaces:**
- Consumes: `VITE_API_MODE=server | mock`, Playwright `testMatch`, npm argument forwarding, 현재 server 4개·mock 2개 spec allowlist
- Produces: file 목록을 내장하지 않는 `e2e`·`e2e:mock` scripts와 server `testMatch=["**/server-mode-boundary.spec.ts", "**/smoke.spec.ts", "**/auth.spec.ts", "**/accessibility-shell.spec.ts"]`, mock `testMatch=["**/mock-preview-shell.spec.ts", "**/mock-mode-boundary.spec.ts"]`; CLI file filter와 mode allowlist의 교집합 실행 계약
**TDD 절차:**
- [x] **RED: 실패 테스트 작성/실패 확인** — mode boundary test가 npm scripts의 고정 spec 목록 제거와 config의 mode별 exact allowlist를 요구하게 하고 `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts`가 현재 script/config로 실패하는지 확인한다.
- [x] **RED: surface 실패 확인** — network 재실패 retry E2E를 추가하고 현재 구현에서 동작을 확인하되, `npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts`가 불필요한 4 files를 수집해 focused list assertion을 실패시키는지 확인한다.
- [x] **GREEN: 최소 구현/통과 확인** — script는 각각 `VITE_API_MODE=server playwright test`, `VITE_API_MODE=mock playwright test`만 유지하고 config의 `apiMode``testMatch`에 Interfaces의 exact glob allowlist를 옮겨 contract·focused list·network retry E2E를 통과시킨다.
- [x] **REFACTOR: 정리/회귀 확인** — mode allowlist 상수와 README/scripts 문구를 정리한 뒤 focused·bare E2E와 P2 Gate를 실행한다.
- [x] TDD 단계와 아래 검증 기준의 실제 결과를 plan/review에 누적한다.
**검증 기준:**
- **실행 명령:** `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts`, `npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts`, `npm run e2e:mock -- --list tests/e2e/mock-preview-shell.spec.ts`, `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts`, `npm run e2e`, `npm run e2e:mock`, `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx`, `npm run typecheck`, `npm run lint`
- **기대 결과:** focused server boundary 1 file / 16 tests 이상, focused mock preview 1 file / 24 tests, bare server 4 files / 36 tests 이상, bare mock 2 files / 28 tests, P2 focused 7 files / 25 tests 이상, type·lint 오류 0건; network retry 재실패 뒤 retry button 1건, 보호 `main`·logout·mock worker 0건.
- **수동 확인:** 없음 — mode/file 수집 목록, network retry와 worker 경계는 Playwright list·실행 결과로 결정적으로 검증한다.
### Task R2.14 — P2-R12 test 증거 정합성 복구
**Goal 실행 `P2-R14`:** P2-R12의 실제 test case 구조·실행 수를 plan/review의 완료 증거와 사실대로 정렬한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-017`
- 시작 조건:
- `P2-R11`~`P2-R13` 구현·검증 완료.
- `rg -c '^test\(' src/app/App.test.tsx`와 focused 실행이 모두 27 tests를 반환한다.
- 최초 pending은 전용 test 1건, retry pending은 기존 404 retry test의 assertion 보강이지만 `P2-R12` 체크와 수정 기록은 두 신규 test·28 tests 이상으로 표시한다.
- 완료 증거:
- P2-R12의 현재 완료 증거가 최초 pending 전용 test 1건 추가 + 기존 retry test 보강 + App 27 tests로 일치
- 이전 “두 신규 test·28 tests 이상” 표현과 정정 사유를 새 Progress에 보존하고 실제 실행 결과를 덮어쓰지 않음
- plan/review 검색·diff 검사와 App focused test 결과를 수정 검증 기록에 누적
- 범위 밖:
- 애플리케이션 코드·test·설정 변경
- 같은 retry pending 동작을 중복 검증하는 test case 추가
- 기존 실행 이력 삭제 또는 제품 동작 완료 주장 변경
**Files:**
- Modify: `docs/20260725_AI캐릭터관리자웹/plan-task.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- Test: 없음 — 구현·test 동작은 바꾸지 않고 완료 문서의 사실관계와 현재 상태만 정정한다.
**Interfaces:**
- Consumes: `REV-P2-017`, `src/app/App.test.tsx`의 27 test declarations, `P2-R12` 완료 기록
- Produces: actual test case와 검증 시나리오를 구분한 P2-R12 완료 증거와 정정 기록
**TDD 예외 사유:** 제품 코드와 test를 변경하지 않는 문서 정정이며, 새 test case를 추가하면 이미 존재하는 retry pending assertion을 중복하게 된다. 실제 test 선언·focused 결과와 문서 문자열 대조를 실패·성공 증거로 사용한다.
**대체 검증 방법:** App test 선언 수·두 pending 시나리오의 위치·focused 실행 수를 plan/review 문구와 대조하고, stale 표현 검색과 문서 diff 검사를 실행한다.
- [x] `rg -c '^test\(' src/app/App.test.tsx`와 두 pending test 위치를 수집해 27 tests 구조를 재확인한다.
- [x] P2-R12 체크·기대 결과·수정 검증 기록을 최초 전용 test 1건 + 기존 retry test 보강 + App 27 tests로 정정하고 이전 표현·정정 사유를 새 기록에 남긴다.
- [x] review 상단·발견 요약·종료 판정을 `REV-P2-017` 수정 완료와 `P2-R14` 완료 상태로 갱신한다.
- [x] 아래 대체 검증과 문서 diff 검사를 실행하고 실제 결과를 plan/review에 누적한다.
**P2-R14 수정 검증 기록 (2026-07-27):**
- 대체 RED: `rg -c '^test\(' src/app/App.test.tsx` — 27건. `rg -n 'initial protected route probe is pending|retries a protected route 404' src/app/App.test.tsx` — 최초 pending 전용 test 1건과 기존 404 retry test 1건을 확인했다. 기존 P2-R12 기록은 두 신규 test·App 28 tests 이상으로 표시돼 실제 구조와 불일치했다.
- GREEN: P2-R12 TDD 절차·기대 결과와 P2-R11~P2-R13 수정 검증 기록을 최초 pending 전용 test 1건 + 기존 retry test 보강 + App 27 tests로 정정했다. 이전 잘못된 표현과 정정 사유는 이 P2-R14 기록과 review `REV-P2-017`에 보존했다.
- Focused: `npm run test:run -- src/app/App.test.tsx` — 1 file / 27 tests 통과.
- 검증: `rg -n '^[[:space:]]+- P2-R12 RED:.*신규 pending status tests 2건' docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — no matches. `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
**검증 기준:**
- **실행 명령:** `rg -c '^test\(' src/app/App.test.tsx`, `rg -n 'initial protected route probe is pending|retries a protected route 404' src/app/App.test.tsx`, `npm run test:run -- src/app/App.test.tsx`, `rg -n '^[[:space:]]+- P2-R12 RED:.*신규 pending status tests 2건' docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`, `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- **기대 결과:** App test 선언·focused 실행 27건, 최초 pending 전용 test와 기존 retry test 각 1건 확인, 정정 대상인 잘못된 RED 기록 검색 결과 0건, 문서 whitespace 오류 0건.
- **수동 확인:** plan/review가 test case 수와 검증 시나리오 수를 구분하고 P2-R12의 실제 완료 증거를 같은 의미로 표시한다.
### Task R2.15 — review plan 전환 절의 현재 수정 상태 정합성 복구
**Goal 실행 `P2-R15`:** Phase 2 review의 plan·goal 전환 절이 `P2-R8`~`P2-R13`의 현재 수정 완료 상태를 요약·상세·종료 판정과 일치하게 표시하도록 정정한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-018`
- 시작 조건:
- review 요약·상세·종료 판정은 `REV-P2-010`~`REV-P2-016``P2-R8`~`P2-R13`을 수정 완료로 표시한다.
- 같은 review의 `7. 확정 항목의 plan·goal 전환`은 3차·4차 회귀 Task를 여전히 “아직 수정하지 않았다”고 표시하는 모순을 확인한다.
- 완료 증거:
- review §7의 `P2-R8`~`P2-R13` 전환 설명이 각 Task의 실제 수정 완료 상태와 일치함
- 과거 시점의 발견·수정 전 검증 기록은 삭제하거나 현재 상태처럼 다시 쓰지 않고 그대로 보존함
- stale 현재 상태 표현 검색 결과 0건과 문서 whitespace 오류 0건
- 범위 밖:
- 애플리케이션 코드·test·설정 변경
- 과거 검증 기록과 당시의 남은 항목 삭제 또는 덮어쓰기
- `P2-R8`~`P2-R13` 구현·검증의 재수행
**Files:**
- Modify: `docs/20260725_AI캐릭터관리자웹/plan-task.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- Test: 없음 — 완료된 review의 현재 상태 문구만 정정하는 문서 Task다.
**Interfaces:**
- Consumes: `REV-P2-010`~`REV-P2-016`, `P2-R8`~`P2-R13` 수정 검증 기록, review §5·§6·§8
- Produces: review §7과 요약·상세·종료 판정이 같은 현재 상태를 표시하는 Phase 2 인수인계 문서
**TDD 예외 사유:** 제품 동작이나 실행 가능한 계약을 바꾸지 않는 문서 상태 정정이므로 새 제품 test를 추가하지 않는다.
**대체 검증 방법:** review §5·§6·§7·§8의 상태를 직접 대조하고 stale 현재 상태 문자열 검색과 Markdown diff 검사를 실행한다.
- [x] review §7의 3차 회귀 Task 설명을 `P2-R8`~`P2-R10` 수정 완료 상태로 정정한다.
- [x] review §7의 4차 회귀 Task 설명을 `P2-R11`~`P2-R13` 수정 완료 상태로 정정한다.
- [x] 과거 시점의 수정 전 기록과 남은 항목이 보존됐는지 확인한다.
- [x] 아래 대체 검증을 실행하고 실제 결과를 plan/review에 누적한다.
**P2-R15 수정 검증 기록 (2026-07-27):**
- 대체 RED: `rg -n '3차 재검증의 새 확정 4건.*아직 수정하지 않았다|4차 재검증의 새 확정 3건.*아직 수정하지 않았다' docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — 2건. review §7이 완료된 `P2-R8`~`P2-R13`을 여전히 미수정 현재 상태로 표시했다.
- GREEN: review §7의 3차·4차 회귀 Task 설명을 2026-07-27 수정·검증 완료 상태로 정정하고, review의 `REV-P2-018` 상태·발견 요약·종료 판정을 수정 완료로 맞췄다. §9의 당시 남은 항목과 과거 검증 기록은 보존했다.
- 검증: 같은 stale 현재 상태 검색 — no matches. `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
**검증 기준:**
- **실행 명령:** `rg -n '3차 재검증의 새 확정 4건.*아직 수정하지 않았다|4차 재검증의 새 확정 3건.*아직 수정하지 않았다' docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`, `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- **기대 결과:** stale 현재 상태 검색 결과 0건, 문서 whitespace 오류 0건, review §5·§6·§7·§8의 `P2-R8`~`P2-R13` 상태 의미 차이 0건.
- **수동 확인:** review §9의 당시 남은 항목 기록은 이력으로 보존되고 §7의 현재 전환 상태만 수정 완료로 정정됐는지 확인한다.
### Task R2.16 — Phase 2 현재 상태·하단 Progress 정합성 복구
**Goal 실행 `P2-R16`:** Phase 2 상단 현재 상태와 하단 최신 Progress가 `P2-R15`까지의 실제 수정 완료 및 Phase 3 진행 가능 상태를 일치하게 표시하도록 정정한다.
- 연결 리뷰: [Phase 2 리뷰](./reviews/review-phase-2.md) — `REV-P2-019`
- 시작 조건:
- `P2-R15` Task 본문과 review는 수정 완료 및 열린 확정 항목 없음을 표시한다.
- Phase 2 상단 현재 상태는 최초 추가 당시 설명만 유지하고, 하단 최신 Progress는 `P2-R15` 수정 필요 상태로 끝나는 모순을 확인한다.
- 완료 증거:
- Phase 2 상단 현재 상태가 `P2-T1`~`P2-T3`, `P2-GATE`, `P2-R1`~`P2-R16` 완료와 Phase 3 진행 가능 상태를 명시함
- 6차 재검증의 당시 남은 항목은 이력으로 보존하고 하단에 `P2-R16` 수정 검증을 누적함
- 최신 Progress, review 요약·종료 판정과 Phase 2 상단 상태의 의미 차이 0건
- 범위 밖:
- 애플리케이션 코드·test·설정 변경
- 과거 검증 기록과 당시 남은 항목 삭제 또는 덮어쓰기
- Phase 3 기능 구현
**Files:**
- Modify: `docs/20260725_AI캐릭터관리자웹/plan-task.md`
- Modify: `docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md`
- Test: 없음 — 완료된 Phase의 현재 상태와 누적 Progress만 정정하는 문서 Task다.
**Interfaces:**
- Consumes: `REV-P2-019`, `P2-R15` 수정 검증, Phase 2 상단 현재 상태와 하단 Progress
- Produces: Phase 3 시작 조건 판정에 사용할 일관된 Phase 2 완료 상태
**TDD 예외 사유:** 제품 동작이나 실행 가능한 계약을 바꾸지 않는 문서 상태 정정이므로 새 제품 test를 추가하지 않는다.
**대체 검증 방법:** 완료 상태 exact 문자열과 최신 Progress를 검색하고, 과거 6차 기록 보존 및 Markdown diff를 확인한다.
- [x] Phase 2 상단 현재 상태를 실제 완료 범위와 Phase 3 진행 가능 상태로 정정한다.
- [x] 6차 재검증의 당시 남은 항목을 보존하고 하단에 `P2-R16` 수정 검증을 누적한다.
- [x] review의 `REV-P2-019` 상태·plan 전환·종료 판정을 수정 완료로 갱신한다.
- [x] 아래 대체 검증과 Phase 2 전체 Gate를 실행하고 실제 결과를 plan/review에 누적한다.
**P2-R16 수정 검증 기록 (2026-07-27):**
- 대체 RED: Phase 2 완료 상태 exact 검색과 `tail` 기반 최신 Progress 검색은 모두 no match, exit 1이었다. 상단은 최초 추가 설명에 머물고 문서 끝은 `P2-R15` 수정 필요 상태였다.
- GREEN: Phase 2 상단에 `P2-R16`까지 완료 및 Phase 3 진행 가능 상태를 명시하고, 6차 당시 남은 항목은 보존한 채 하단에 `P2-R16` 수정 검증을 누적했다. review의 `REV-P2-019`와 종료 판정도 수정 완료로 정렬했다.
- Phase 2 Gate: `npm run test:run` — 34 files / 147 tests 통과. `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0, build는 각각 160 modules 변환. `VITE_API_MODE=mock npm run build:prod` — 기대한 guard로 exit 1.
- E2E: `npm run e2e` — 4 projects / 36 tests 통과. `npm run e2e:mock` — 4 projects / 28 tests 통과.
- Production: `dist/mockServiceWorker.js` 없음, production JS의 `startMockWorker`·`mockServiceWorker.js` 검색은 기대한 no-match exit 1.
- 문서 검증: Phase 2 완료 상태와 최신 Progress exact 검색, 과거 6차 `P2-R15` 남은 항목 보존 검색, plan/review `git diff --check`를 통과했다.
**검증 기준:**
- **실행 명령:** Phase 2 완료 상태 exact 검색, `tail` 기반 최신 Progress 확인, 과거 `P2-R15` 남은 항목 보존 검색, `git diff --check`, Phase 2 전체 Gate
- **기대 결과:** 현재 상태·최신 Progress 의미 차이 0건, 과거 기록 보존, 문서 whitespace 오류 0건, Phase 2 자동 Gate 0 failure/0 error
- **수동 확인:** Phase 3 실행자가 과거 6차 기록을 열린 현재 Task로 오해하지 않고 `P2-GATE`와 모든 회귀 수정 완료를 확인할 수 있다.
---
## Phase 3. Character workspace vertical slice
@@ -1626,3 +2292,174 @@ assert_no_match "externalCharacterId|SUNDAY|MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FR
- `git diff --check -- <수정 문서 7개>` — 성공.
- 애플리케이션 test/build는 구현 코드와 설정을 변경하지 않았고 신규 명령도 아직 계획 상태이므로 실행하지 않는다.
- 남은 항목: Phase 2 구현 시 실제 `dev:mock`·`e2e:mock` 명령과 환경 변수를 만든 후 README와 `docs/agent-guide/{environment,scripts}.md`를 실체에 맞게 갱신한다. 계약 미제공 도메인은 backend 계약 수신 전 fixture를 만들지 않는다.
### Phase 2 코드 리뷰·QA — 2026-07-27
- 무엇을: Phase 2 staged 구현 28개 경로를 PRD `MOCK-001~009`, API Contract §1·§3, `P2-T1~P2-GATE`와 대조하고 확정 문제 5건을 `P2-R1~P2-R3`으로 전환했다.
- 왜: 자동 검증 통과와 별개로 보호 route fail-closed, exact API origin, invalid·revoked JWT status, Phase 번호 표시와 완료 문서 추적성이 실제 계약과 일치하는지 독립적으로 판정하기 위해서다.
- 어떻게:
- `npm run test:run` — 성공, 34 files / 130 tests passed.
- `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 성공, 7 files / 20 tests passed.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod`, `git diff --check`, `git diff --cached --check` — 모두 성공.
- `VITE_API_MODE=mock npm run build:prod` — 기대한 거부, exit 1과 `VITE_API_MODE=mock is only available during development` 확인.
- `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 최초 sandbox listen EPERM, 권한 허용 후 성공, 4 projects / 20 tests passed.
- `npm run e2e:mock -- tests/e2e/mock-mode-boundary.spec.ts` — 성공, 4 projects / 4 tests passed.
- `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts` — 성공, 4 projects / 12 tests passed. 이 통과 과정에서 404·network error alert가 보호 shell 내부에서 렌더되는 회귀를 별도 확정했다.
- `npm run e2e -- tests/e2e/smoke.spec.ts tests/e2e/auth.spec.ts tests/e2e/accessibility-shell.spec.ts` — 성공, 4 projects / 20 tests passed.
- Vite SSR로 `createMockHandlers(createMockStore())`를 실행한 재현 — wrong-origin login 200, invalid JWT logout 200, 정상 logout 200, revoked JWT 재logout 200을 확인했다. Vite HMR WebSocket은 sandbox listen EPERM 경고가 있었으나 MSW request 재현 command는 exit 0으로 완료됐다.
- 남은 항목: `REV-P2-001~005`를 수정하는 `P2-R1~P2-R3`. 기존 Phase 2 완료 체크와 검증 기록은 되돌리지 않는다.
### Phase 2 구현·Gate 완료 기록 — 2026-07-27
- 무엇을: explicit `server | mock` mode, 개발 전용 browser MSW bootstrap, auth preview fixture, mock banner, no-auto-fallback E2E, production mock 차단과 실행 문서를 구현했다.
- 왜: backend endpoint 구현 전에도 제공 API Contract 범위의 최종 UI를 mock mode에서 확인하되, 기본 server mode와 production build가 mock으로 자동 대체되지 않게 하기 위해서다.
- 어떻게:
- P2-T1 focused unit·boundary E2E, P2-T2 auth handler/banner/mock preview E2E, P2-T3 docs/accessibility E2E와 P2-GATE를 2026-07-27 본문 기록대로 실행했다.
- P2-GATE 기준 `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx`는 7 files / 20 tests 통과했다.
- `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts`는 4 browser projects / 20 tests 통과했고 mock login, protected shell, logout 후 재login, 320px·200% zoom, axe critical·serious 0건을 확인했다.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod`는 모두 exit 0이었다.
- Phase 2 코드 리뷰에서 열린 `REV-P2-001~005`는 이 완료 기록을 삭제하지 않고 `P2-R1~P2-R3` 회귀 Task로 별도 추적한다.
- 남은 항목: `P2-R1~P2-R3` 수정 완료 전에는 Phase 2 리뷰를 닫지 않는다. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### Phase 2 3차 코드 리뷰·QA — 2026-07-27
- 무엇을: `P2-R4`~`P2-R7` 수정 뒤의 현재 working tree를 재검토해 `REV-P2-010` Low 1건과 `REV-P2-011~013` Medium 3건을 확정하고 `P2-R8`~`P2-R10` 회귀 Task로 전환했다.
- 왜: 완료 문서가 최종 변경 경로를 추적하는지, Mock Preview banner 이동 뒤 mobile menu의 background inert·responsive breakpoint 전환과 보호 route 오류 recovery가 유지되는지 확인하기 위해서다.
- 어떻게:
- `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 7 files / 25 tests 통과.
- `npm run test:run` — 34 files / 141 tests 통과.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0.
- `VITE_API_MODE=mock npm run build:prod` — 기대대로 exit 1. production 산출물의 worker 파일·mock bootstrap 문자열은 0건.
- `npm run e2e` — 4 projects / 32 tests 통과. `npm run e2e:mock` — 4 projects / 24 tests 통과.
- Chromium one-off — login과 Character probe 응답이 모두 `fromServiceWorker=true`임을 확인했다. 320px menu open에서는 banner가 inert 경계 밖에 있었고, 1,200px 전환 뒤 overlay는 숨겨졌지만 main inert·`aria-hidden=true`가 남았다.
- 코드·test 대조 — 보호 route 오류 page의 interactive recovery control 0건과 App/server boundary retry test 0건을 확인했다.
- `git diff HEAD --name-only | wc -l` — 37개 경로, `git diff --check HEAD` — exit 0.
- 문서 반영 검증 — `npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts`는 1 file / 3 tests 통과, review 상세 ID 13개·Phase 2 회귀 Task 10개를 확인했고 stale 3차 범위 문자열과 문서 whitespace 오류는 0건이었다.
- 변경 범위: [Phase 2 리뷰](./reviews/review-phase-2.md)에 3차 근거·발견·판정을 누적하고 이 문서에 `P2-R8`~`P2-R10`만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
- 남은 항목: `P2-R8` 문서 추적성, `P2-R9` mobile menu 반응형·inert, `P2-R10` 보호 route 오류 retry 복구. 세 goal의 대체 검증 또는 RED/GREEN/REFACTOR, 관련 E2E와 P2 Gate가 끝나기 전에는 Phase 2 리뷰를 닫지 않는다.
### P2-R9 수정 검증 — 2026-07-27
- 무엇을: `REV-P2-011`, `REV-P2-012``P2-R9` 범위에서 수정했다. Mock Preview 성공 shell의 banner를 `ProtectedAdminShell` background inert container 안으로 옮기고, native `matchMedia('(min-width: 1024px)')` change에서 mobile menu state와 inert·`aria-hidden`을 해제하되 숨겨진 mobile trigger로 focus를 복귀하지 않게 했다.
- 왜: mobile menu가 열린 상태에서 background 전체가 같은 접근성 차단 경계에 속해야 하며, `lg` 이상 viewport로 전환될 때 보이는 desktop navigation·logout·main이 즉시 다시 조작 가능해야 하기 때문이다.
- 어떻게:
- RED unit: `npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 2 files 중 `App.test.tsx` 2 tests가 기대대로 실패했다. 실패 핵심은 `mock banner inert background not found``모바일 주 메뉴` 잔존이다.
- RED e2e: `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 4 browser projects에서 신규 mock shell test가 모두 기대대로 실패했다. 실패 핵심은 320px open state의 `bannerInert=false`, `bannerHidden=false`, `mainInert=true` 불일치다.
- GREEN unit: 같은 focused unit command — 2 files / 26 tests 통과.
- GREEN e2e: 같은 mock e2e command — 28 tests 통과. 320px open state에서 banner·main이 같은 inert/`aria-hidden` 경계에 있고 1,024px·1,200px 전환 뒤 mobile menu가 제거되며 desktop navigation·logout이 보이는 것을 확인했다.
- Regression: `npm run e2e -- tests/e2e/accessibility-shell.spec.ts` — 32 tests 통과. `npm run typecheck`, `npm run lint` — 모두 exit 0.
- LSP diagnostics: `src/app/App.tsx`, `src/app/protected-admin-shell.tsx`, `src/app/App.test.tsx`, `tests/e2e/mock-preview-shell.spec.ts` 모두 0 diagnostics.
- P2 focused Gate: `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — P2-R9 문서 기록 반영 전에는 docs contract 1 test가 실패했다. 기록 반영 후 재실행해 7 files / 25 tests 통과했다.
- 남은 항목: `P2-R10` 보호 route 오류 retry 복구. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### P2-R10 수정 검증 — 2026-07-27
- 무엇을: `REV-P2-013``P2-R10` 범위에서 수정했다. 보호 route 오류 page에 native retry button을 추가하고, 현재 session·route visit·retry attempt가 모두 일치할 때만 오류나 검증 성공을 사용하도록 했다. 정상 `ApiError`의 서버 message는 유지하고 network 오류에는 안전한 공통 안내를 표시한다.
- 왜: 404·network 오류 뒤에도 사용자가 browser refresh 없이 복구할 수 있어야 하며, 현재 수동 retry가 성공하기 전에는 보호 shell·navigation·logout을 계속 숨겨야 하기 때문이다.
- 어떻게:
- RED unit: `npm run test:run -- src/app/App.test.tsx` — 1 file / 26 tests 중 신규 2 tests가 `보호 route 다시 시도` button 부재로 기대대로 실패했고 24 tests는 통과했다.
- RED E2E: `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts` — 32 tests 중 신규 retry 시나리오가 4 browser project에서 button 부재로 기대대로 실패했고 28 tests는 통과했다.
- GREEN unit: 같은 App command — 1 file / 26 tests 통과. 404 `없습니다.` 보존, network 재실패의 공통 안내·retry 유지, pending 중 shell·logout 비노출과 성공 뒤 shell 표시를 확인했다.
- GREEN E2E: 같은 server command — 4 projects / 32 tests 통과. 명시적 retry 전후 browser MSW worker 0건과 성공 전 shell·logout 0건을 확인했다.
- Regression: `npm run test:run -- src/shared/config src/shared/mocks src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 7 files / 25 tests 통과. `npm run test:run` — 34 files / 145 tests 통과.
- Static/build: `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0. no-excuse 검사도 변경 TS/TSX 3 files / 위반 0건이었다.
- LSP diagnostics: `src/app` 4 files, `tests/e2e` 6 files에서 diagnostics 0건.
- 수동·시각 확인: server mode 실제 브라우저 375px·768px·1,280px에서 button 높이 44px, `:focus-visible=true`, 한국어 clipping·비정상 줄바꿈 0건을 확인했다. retry 성공 뒤 shell·logout 표시와 mock worker 0건을 확인했고 기능 무결성·CJK 정밀 검토가 모두 PASS였다.
- Diff: `git diff --check -- src/app/App.tsx src/app/App.test.tsx tests/e2e/server-mode-boundary.spec.ts docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
- 남은 항목: `P2-R10` 범위 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### Phase 2 4차 코드 리뷰·QA — 2026-07-27
- 무엇을: `P2-R8`~`P2-R10` 반영 뒤 working tree 전체 집계, 보호 route pending UI와 mode별 focused E2E 증거를 재검토해 `REV-P2-014` Low 1건과 `REV-P2-015~016` Medium 2건을 확정하고 `P2-R11`~`P2-R13`으로 전환했다.
- 왜: 완료 판정이 untracked 파일, 300ms 이상 retry 대기 상태와 Task가 지정한 단일 E2E spec의 실제 수집 범위를 빠뜨리지 않는지 확인하기 위해서다.
- 어떻게:
- `npm run test:run -- src/app/App.test.tsx src/shared/ui/__tests__/mock-mode-banner.test.tsx` — 2 files / 28 tests 통과. `npm run test:run` — 34 files / 145 tests 통과.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0, build는 각 160 modules 변환.
- `VITE_API_MODE=mock npm run build:prod` — 기대대로 exit 1. production worker 파일과 mock bootstrap 문자열은 0건.
- `npm run e2e` — 4 projects / 32 tests 통과. `npm run e2e:mock` — 4 projects / 28 tests 통과.
- `git diff HEAD --name-only | wc -l` — tracked 37개, `git status --porcelain=v1 | wc -l` — 전체 38개, `git ls-files --others --exclude-standard``src/app/protected-admin-shell.tsx` 1개.
- `npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts` — 4 files / 32 tests, 직접 `VITE_API_MODE=server npx playwright test tests/e2e/server-mode-boundary.spec.ts --list` — 1 file / 12 tests. 고정 script가 focused filter를 무효화하고 `P2-R10` 기대 16 tests 미달을 가리는 것을 확인했다.
- Chromium one-off — 404 retry 성공 응답을 350ms 지연했을 때 `#root` child·`main`·`status`·`alert` 0개, active element `BODY`; 응답 뒤 shell·logout 표시를 확인했다. 최초 sandbox local listen·browser launch 실패는 권한 허용 재실행으로 보완했다.
- 문서 반영 검증 — `npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts`는 1 file / 3 tests 통과. review 상세 ID 16개, Phase 2 회귀 Task 13개와 문서 whitespace 오류 0건을 확인했다.
- 변경 범위: [Phase 2 리뷰](./reviews/review-phase-2.md)에 4차 근거·발견·판정을 누적하고 이 문서에 `P2-R11`~`P2-R13`만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
- 남은 항목: `P2-R11` working tree 추적성, `P2-R12` 보호 route pending 피드백, `P2-R13` focused E2E filter·network retry 증거. 세 goal의 대체 검증 또는 RED/GREEN/REFACTOR와 관련 Gate가 끝나기 전에는 Phase 2 리뷰를 닫지 않는다.
### P2-R11~P2-R13 수정 검증 — 2026-07-27
- 무엇을: `REV-P2-014~016``P2-R11`~`P2-R13` 범위에서 수정했다. working tree 범위는 tracked 37개 + untracked `src/app/protected-admin-shell.tsx` 1개로 구분해 기록하고, 보호 route pending에는 기존 `PageState` loading status를 표시했다. E2E script의 spec allowlist는 `playwright.config.ts` `testMatch`로 옮기고 network retry 재실패 E2E를 추가했다.
- 왜: 완료 증거가 untracked 파일, 접근 가능한 300ms 이상 pending feedback, focused E2E file filter와 실제 network retry 경계를 빠뜨리지 않게 하기 위해서다.
- 어떻게:
- P2-R11 대체 검증: `git diff HEAD --name-only | wc -l` — tracked 37개, `git status --short --untracked-files=all | wc -l` — 전체 38개, `git ls-files --others --exclude-standard``src/app/protected-admin-shell.tsx` 1개.
- P2-R12 RED: `npm run test:run -- src/app/App.test.tsx` — 최초 pending 전용 test 1건과 기존 404 retry test의 pending assertion이 `role="status"` 부재로 기대대로 실패했다.
- P2-R12 GREEN: 같은 command — 1 file / 27 tests 통과. 최초 probe와 retry pending 중 `role="status"`, 보호 `main`·logout 0건을 확인했다.
- P2-R13 RED: `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts` — npm script와 Playwright config contract 2 tests가 기대대로 실패했다.
- P2-R13 GREEN focused: `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 2 files / 6 tests 통과. `npm run e2e -- --list tests/e2e/server-mode-boundary.spec.ts` — 1 file / 16 tests, `npm run e2e:mock -- --list tests/e2e/mock-preview-shell.spec.ts` — 1 file / 24 tests.
- Surface: `npm run e2e -- tests/e2e/server-mode-boundary.spec.ts` — 16 tests 통과. `npm run e2e:mock -- tests/e2e/mock-preview-shell.spec.ts` — 24 tests 통과.
- LSP diagnostics: `src/app/App.tsx`, `src/app/App.test.tsx`, `playwright.config.ts`, `src/shared/mocks/__tests__/mode-boundary.test.ts` 0 diagnostics. `tests/e2e/server-mode-boundary.spec.ts` 단일 fresh diagnostics는 timeout이었고 focused E2E와 typecheck로 보완한다.
- Final Gate: `npm run test:run` — 34 files / 147 tests 통과. `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0. `npm run e2e` — 36 tests 통과, `npm run e2e:mock` — 28 tests 통과.
- Diff: `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md tests/e2e/server-mode-boundary.spec.ts package.json playwright.config.ts README.md docs/agent-guide/scripts.md src/app/App.tsx src/app/App.test.tsx src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — exit 0.
- 남은 항목: `P2-R11`~`P2-R13` 범위 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### Phase 2 5차 코드 리뷰·QA — 2026-07-27
- 무엇을: `P2-R11`~`P2-R13` 반영 뒤 실제 test case 구조·실행 수와 plan/review의 완료 상태를 대조해 `REV-P2-017` Low 1건을 확정하고 `P2-R14` 회귀 Task로 전환했다.
- 왜: 동작 검증 통과와 별개로 TDD 완료 체크·검증 수치가 실제 구현과 같은 사실을 표시하는지 확인하기 위해서다.
- 어떻게:
- `npm run test:run -- src/app/App.test.tsx` — 1 file / 27 tests 통과. `rg -c '^test\(' src/app/App.test.tsx` — 27건.
- 코드 대조 — 최초 probe pending 전용 test 1건을 추가했고 retry pending은 기존 404 retry test에 assertion을 보강했음을 확인했다. `P2-R12`의 두 신규 test·App 28 tests 이상 완료 체크와 불일치한다.
- `npm run test:run -- src/shared/mocks/__tests__/mode-boundary.test.ts src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 2 files / 6 tests 통과. `npm run test:run` — 34 files / 147 tests 통과.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0. `VITE_API_MODE=mock npm run build:prod` — 기대한 production guard로 exit 1.
- focused E2E list는 server 1 file / 16 tests, mock 1 file / 24 tests. 최초 sandbox listen `EPERM` 뒤 허용된 로컬 실행에서 server boundary 16, mock preview 24, bare server 36, bare mock 28 tests가 모두 통과했다.
- working tree는 tracked diff 37개 + untracked `src/app/protected-admin-shell.tsx` 1개 = 전체 38개 항목으로 유지됐다.
- 변경 범위: [Phase 2 리뷰](./reviews/review-phase-2.md)에 `REV-P2-017`과 5차 근거·판정을 누적하고 이 문서에 `P2-R14`만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
- 남은 항목: `P2-R14` 범위 없음. mock 통과는 후속 도메인 server integration 완료로 간주하지 않는다.
### P2-R14 수정 검증 — 2026-07-27
- 무엇을: `REV-P2-017``P2-R14` 범위에서 수정했다. P2-R12 완료 증거를 실제 App test 구조인 최초 pending 전용 test 1건 + 기존 404 retry test의 pending assertion 보강 + App 27 tests로 정정했다.
- 왜: 문서가 “두 신규 test·28 tests 이상”을 완료 조건처럼 표시하면 후속 reviewer가 실제 test case 수와 검증 시나리오 수를 혼동하기 때문이다.
- 어떻게:
- 대체 RED: `rg -c '^test\(' src/app/App.test.tsx` — 27건. `rg -n 'initial protected route probe is pending|retries a protected route 404' src/app/App.test.tsx` — 최초 pending 전용 test와 기존 404 retry test 위치를 확인했다.
- GREEN docs: P2-R12 TDD 절차·기대 결과, P2-R11~P2-R13 수정 검증 기록, review 요약·종료 판정을 실제 구조와 일치시켰다.
- Focused: `npm run test:run -- src/app/App.test.tsx` — 1 file / 27 tests 통과.
- 문서 검증: stale `신규 pending status tests 2건` 검색 결과 0건, `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
- 남은 항목: Phase 2 review의 열린 확정 항목 없음. 애플리케이션 코드·test·설정은 변경하지 않았다.
### Phase 2 6차 코드 리뷰·QA — 2026-07-27
- 무엇을: `P2-R14` 반영 뒤 Phase 2 구현·test·production·mode별 E2E와 review 현재 상태를 다시 대조해 `REV-P2-018` Low 1건을 확정하고 `P2-R15`로 전환했다.
- 왜: review 요약·상세·종료 판정과 plan 전환 절이 완료된 회귀 Task의 현재 상태를 같은 의미로 표시하는지 확인하기 위해서다.
- 어떻게:
- `npm run test:run` — 34 files / 147 tests 통과.
- `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0, build는 각각 160 modules 변환.
- `VITE_API_MODE=mock npm run build:prod` — 기대한 production guard로 exit 1.
- `npm run e2e` — 최초 sandbox listen `EPERM` 뒤 로컬 실행 권한으로 재실행해 4 projects / 36 tests 통과.
- `npm run e2e:mock` — 최초 sandbox listen `EPERM` 뒤 로컬 실행 권한으로 재실행해 4 projects / 28 tests 통과.
- Chromium one-off — login·보호 route 응답 `fromServiceWorker=true`, 320px menu·logout 높이 60px, mobile link 선택 뒤 menu·background inert 잔존 0건을 확인했다.
- `git diff --check HEAD` — review 문서 반영 전 exit 0. review §7의 `P2-R8`~`P2-R13` “아직 수정하지 않았다” 2건과 §5·§6·§8의 수정 완료 상태가 모순됨을 확인했다.
- 문서 반영 후 `npm run test:run -- src/shared/mocks/__tests__/mock-preview-docs.test.ts` — 1 file / 3 tests 통과, plan/review 대상 `git diff --check` — exit 0.
- 변경 범위: 이 review 문서와 `plan-task.md``REV-P2-018`, `P2-R15` 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
- 남은 항목: `P2-R15`에서 review §7의 현재 상태 문구를 정정하고 문서 검색·diff 검증을 누적해야 한다.
### Phase 2 7차 코드 리뷰·QA — 2026-07-27
- 무엇을: `P2-R15` 반영 뒤 Phase 2 상단 현재 상태, Task 본문, review 종료 판정과 하단 최신 Progress를 대조해 `REV-P2-019` Low 1건을 확정하고 `P2-R16`으로 전환했다.
- 왜: 완료된 회귀 Task의 inline 기록뿐 아니라 Phase 현재 상태와 하단 누적 Progress도 Phase 3 실행자가 같은 완료 상태로 해석할 수 있어야 하기 때문이다.
- 어떻게:
- 대체 RED 완료 상태 검색 — Phase 2 상단의 `P2-R15`까지 완료·Phase 3 진행 가능 exact 상태가 없어 exit 1.
- 대체 RED 최신 Progress 검색 — 문서 끝이 6차 재검증의 `P2-R15` 수정 필요 상태로 끝나 현재 완료 기록이 없어 exit 1.
- 코드 회귀 기준은 6차 재검증 직후 독립 확인에서 unit 147 tests, server E2E 36 tests, mock E2E 28 tests, typecheck·lint·dev/prod build 통과와 production mock guard 거부를 확인했다.
- 변경 범위: 이 review 문서와 `plan-task.md``REV-P2-019`, `P2-R16` 판정·후속 계획만 추가했다. 애플리케이션 코드·test·설정은 변경하지 않았다.
- 남은 항목: `P2-R16`에서 Phase 2 현재 상태와 최신 Progress를 정정하고 문서·Phase 2 Gate 검증을 누적해야 한다.
### P2-R16 수정 검증 — 2026-07-27
- 무엇을: `REV-P2-019`를 수정했다. Phase 2 상단 현재 상태를 `P2-R16`까지 완료 및 Phase 3 진행 가능으로 갱신하고, 6차 당시 남은 항목을 보존한 채 최신 수정 검증을 누적했다.
- 왜: Phase 3 실행자가 완료된 `P2-R15`를 열린 선행 작업으로 오해하지 않고 Phase 2의 실제 완료 상태를 단일하게 판정할 수 있어야 하기 때문이다.
- 어떻게:
- 대체 GREEN: Phase 2 완료 상태 exact 검색과 최신 Progress 검색이 각각 1건 이상 일치했다. 6차 당시 `P2-R15` 남은 항목도 이력으로 보존됐다.
- Full unit/static/build: `npm run test:run` — 34 files / 147 tests 통과. `npm run typecheck`, `npm run lint`, `npm run build:dev`, `npm run build:prod` — 모두 exit 0, build는 각각 160 modules 변환.
- Production boundary: `VITE_API_MODE=mock npm run build:prod` — 기대대로 exit 1. production worker 파일과 mock bootstrap 문자열은 0건.
- E2E: `npm run e2e` — 4 projects / 36 tests 통과. `npm run e2e:mock` — 4 projects / 28 tests 통과.
- Diff: `git diff --check -- docs/20260725_AI캐릭터관리자웹/plan-task.md docs/20260725_AI캐릭터관리자웹/reviews/review-phase-2.md` — exit 0.
- 남은 항목: Phase 2 review의 열린 확정 항목 없음. `P2-GATE`와 모든 회귀 수정이 완료돼 Phase 3 진행 가능. mock 통과는 Phase 3의 실제 server integration 완료로 간주하지 않는다.

View File

@@ -15,7 +15,7 @@
리뷰 시작 시 워킹 트리는 사용자 작업을 포함해 이미 변경 상태였다. 기존 변경은 리뷰 기준선으로 보존했으며, 코드와 기존 문서는 수정하지 않았다. 이 파일만 신규 리뷰 산출물로 작성했다.
리뷰 파일 위치는 같은 PRD 디렉터리를 기본값으로 삼는 일반 규칙보다 구체적인 사용자 지시를 적용해 docs/20260725_AI캐릭터관리자웹/review 아래로 정했다.
리뷰 파일은 현재 저장 규칙에 따라 `docs/20260725_AI캐릭터관리자웹/reviews/` 아래에 둔다.
## 2. 리뷰 목적과 범위

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,10 @@
- PRD를 작성·변경할 때는 [PRD 작성 및 유지보수 규칙](./prd.md)과 [PRD 샘플](../sample/sample-prd.md)을 따른다.
- 구현 항목은 기능/작업 단위로 분리해 체크박스(`- [ ]`) 목록으로 작성한다.
- 구현 완료 시마다 체크박스를 `- [x]`로 갱신하고, 각 항목이 정상 구현되었는지 확인한다.
- `plan-task.md`의 각 Task는 TDD 적용 여부를 명시한다. 테스트 가능한 구현 Task에는 `TDD 절차`를 두고 `RED: 실패 테스트 작성/실패 확인`, `GREEN: 최소 구현/통과 확인`, `REFACTOR: 정리/회귀 확인` 순서를 적는다.
- 실패 테스트 작성이 현실적으로 불가능한 Task는 TDD 절차를 아무 표시 없이 생략하지 말고 같은 Task에 `TDD 예외 사유``대체 검증 방법`을 구체적으로 기록한다.
- 각 Task에는 `실행 명령`, `기대 결과`, `수동 확인`을 포함한 검증 기준을 작성한다. 수동 확인이 불필요하면 `없음`과 그 사유를 적는다.
- 각 Phase의 Gate에도 통합 검증을 위한 실행 명령, 기대 결과, 수동 확인 항목을 작성한다.
- 작업 도중 범위가 변경되면 계획 문서의 체크박스 항목을 먼저 업데이트한 뒤 구현을 진행한다.
- 모든 구현이 끝난 후 결과 보고 시 계획 문서 맨 아래에 무엇을, 왜, 어떻게 검증했는지 한국어로 간단히 기록한다.
- 후속 수정이 발생해도 기존 검증 기록은 삭제/덮어쓰지 않고 누적한다(예: `1차 구현`, `2차 수정`).
@@ -14,4 +18,5 @@
- 단계별 `어떻게`에는 실제 실행한 검증 명령과 결과(성공/실패/불가 사유)를 함께 기록한다.
- 기존 기록 정정이 필요하면 원문을 지우지 말고 `정정` 항목을 추가해 사유와 변경 내용을 남긴다.
- goal 기능으로 실행할 구현 계획은 [Goal 실행형 구현 계획 규칙](./goal-plan.md)과 [Goal 실행형 계획 샘플](../sample/sample-plan-task.md)을 따른다.
- 완료된 Phase 또는 Task의 코드 리뷰·QA 결과 문서는 해당 `prd.md`와 같은 디렉터리에 두고, 상세 형식과 후속 처리에는 [코드 리뷰 및 QA 기록 규칙](./review.md)을 따른다.
- 완료된 Phase 또는 Task의 코드 리뷰·QA 결과 문서는 해당 `prd.md`·`plan-task.md` 디렉터리 아래 `reviews/`에 모아 둔다. 기능 문서 디렉터리 바로 아래나 단수형 `review/`에는 두지 않는다.
- 리뷰 문서의 상세 형식, 파일명과 참조 방법은 [코드 리뷰 및 QA 기록 규칙](./review.md)을 따른다.

View File

@@ -2,4 +2,9 @@
- 개발 서버 API: `VITE_API_BASE_URL=https://test-character-admin.sodalive.net`
- 프로덕션 서버 API: `VITE_API_BASE_URL=https://character-admin.sodalive.net`
- API mode: `VITE_API_MODE=server | mock`. 누락 시 `server`이며, `mock`은 개발 환경에서만 허용한다.
- Vite mode별 파일은 `.env.development`, `.env.production`을 사용한다.
- 기본 `npm run dev``server` mode로 실제 개발 API를 사용하고, `npm run dev:mock`만 browser MSW를 시작한다.
- production build에서 `VITE_API_MODE=mock`은 시작 전에 오류로 거부한다.
- mock data reset: mock data는 browser storage에 영구 저장하지 않고 새 mock store/session이 시작될 때 seed 기준으로 초기화한다.
- no-auto-fallback: server mode의 404 또는 network error를 mock mode로 자동 전환하지 않는다.

View File

@@ -36,6 +36,8 @@
- 하나 이상의 Task
- Task 전체 완료 조건
- 자동·수동 검증 방법과 Phase Gate
- 각 Task의 TDD 절차 또는 TDD 예외 사유와 대체 검증 방법
- 각 Task와 Phase Gate의 실행 명령, 기대 결과, 수동 확인 항목
## 4. Task와 goal 작성 규칙
@@ -43,8 +45,17 @@
- 모든 Task에는 고유 Goal ID, 한 문장 objective, 시작 조건, 완료 증거와 범위 밖을 둔다.
- Goal ID는 `P<Phase>-T<Task>`를 사용한다. Phase Gate는 `P<Phase>-GATE`, 완료 범위의 회귀 수정은 `P<Phase>-R<번호>`를 사용한다.
- Task는 독립 reviewer가 이웃 Task와 별도로 승인·거절할 수 있고, 자체 test cycle로 검증할 수 있는 최소 결과 단위로 나눈다.
- Task마다 생성·수정·test 파일의 정확한 경로를 기록한다. 선행 Task contract를 소비하거나 후속 Task에 제공하면 `Interfaces`에 정확한 type·function·component를 기록한다.
- 구현 체크박스는 실패 test 작성 → 의도한 실패 확인 → 최소 구현 → focused test 성공 → 관련 품질 검증 → Progress 기록 순서를 포함한다.
- Task마다 생성·수정·test 파일의 정확한 경로를 기록한다. TDD 예외 Task에 test 파일이 없으면 `Test: 없음`과 사유를 적는다. 선행 Task contract를 소비하거나 후속 Task에 제공하면 `Interfaces`에 정확한 type·function·component를 기록한다.
- 모든 구현 Task의 `TDD 절차`에는 다음 순서와 확인 내용을 명시한다.
- `RED: 실패 테스트 작성/실패 확인` — 검증할 동작과 실패 테스트 파일, 실행 명령, 의도한 실패 결과를 적는다.
- `GREEN: 최소 구현/통과 확인` — 최소 구현 범위와 동일한 테스트 명령의 통과 결과를 적는다.
- `REFACTOR: 정리/회귀 확인` — 동작을 바꾸지 않는 정리 범위와 focused·관련 회귀 테스트 결과를 적는다.
- 실패 테스트 작성이 현실적으로 불가능한 문서화, 조사, 외부 의존 작업 등은 같은 Task에 `TDD 예외 사유``대체 검증 방법`을 명시한다. 단순히 `해당 없음`만 적거나 산출물과 무관한 테스트를 만드는 것으로 대체하지 않는다.
- 각 Task의 `검증 기준`에는 다음을 포함한다.
- `실행 명령`: focused test, 관련 회귀 test, typecheck·lint 또는 TDD 예외의 대체 검증 등 실제 실행할 명령
- `기대 결과`: 종료 코드, 통과할 test 수, 예상 출력 또는 상태 변화
- `수동 확인`: 사용자가 확인할 화면·동작·문서 항목. 불필요하면 `없음`과 사유를 기록한다.
- 구현 체크박스 마지막에는 검증 결과와 `Progress` 기록을 포함한다.
- “적절히 처리”, “나중에 구현”, “위와 동일”처럼 실행자가 다시 추측해야 하는 표현을 사용하지 않는다.
## 5. 완료와 차단 판정
@@ -52,6 +63,8 @@
- 동시에 하나의 미완료 goal만 운용한다. 활성 goal이 있으면 새 goal을 만들지 않고 같은 Task를 이어서 수행한다.
- 사용자가 명시적으로 요청하지 않으면 token budget을 설정하지 않는다.
- 코드 작성이나 일부 test만 끝난 상태는 완료가 아니다. 체크박스, 완료 증거, 실제 검증과 Progress 기록까지 충족한 뒤에만 goal을 `complete`로 갱신한다.
- 구현 Task는 RED/GREEN/REFACTOR 각 단계의 결과가 없으면 완료할 수 없다.
- TDD 예외 Task는 예외 사유와 대체 검증 결과가 없으면 완료할 수 없다.
- Phase의 모든 활성 Task goal을 완료한 뒤 Phase Gate를 별도 goal로 실행한다.
- 외부 계약이나 권한 같은 동일 차단 사유가 최초 시도와 자동 후속을 포함해 3회 연속 반복되고, 문서화·독립 작업 등 의미 있는 진전도 불가능할 때만 goal을 `blocked`로 갱신한다.
- 계약이 없어 안전하게 구현할 수 없으면 추정하지 않는다. 담당 주체·영향·재개 조건을 기록하고 PRD 결정 기록 → API Contract → `plan-task.md` 순서로 제외 또는 후속 결정을 반영한다.
@@ -62,6 +75,7 @@
- 범위나 구현 방식이 바뀌면 코드를 수정하기 전에 관련 체크박스, Files, Interfaces, 완료 증거와 Decision Log를 갱신한다.
- Progress와 Decision Log의 기존 기록은 삭제하거나 덮어쓰지 않는다. 정정은 날짜·사유와 함께 새 기록으로 추가한다.
- 실행한 명령만 기록하고 성공/실패, exit code, test 수 또는 불가 사유를 남긴다.
- 구현 Task의 Progress에는 RED의 의도한 실패, GREEN의 통과, REFACTOR의 회귀 확인 결과를 구분해 기록한다. TDD 예외 Task는 대체 검증의 실제 결과를 기록한다.
- 구현 중 발견한 범위 내 문제는 `발견된 문제`에 기록한다. 완료 범위의 상세 리뷰·QA는 [코드 리뷰 및 QA 기록 규칙](./review.md)에 따라 별도 review 문서로 관리한다.
- Phase 완료 후 현재 상태 표와 체크박스를 갱신하고 Phase Gate의 최신 증거를 Progress에 누적한다.
@@ -75,6 +89,7 @@ goal을 만들기 전에 다음을 확인한다.
- Files와 Interfaces의 이름이 앞뒤 Task에서 일치한다.
- 외부 의존과 안전한 기본값이 구분돼 있다.
- backend 구현 전 UI preview가 필요하면 제공 계약 기반 explicit mock mode와 실제 server integration을 별도 Task·Gate·Progress로 구분하고 404 자동 fallback을 금지한다.
- 실제 검증 명령과 Expected가 구체적이다.
- 각 구현 Task에 RED/GREEN/REFACTOR 절차가 있고, 예외 Task에는 예외 사유와 대체 검증 방법이 있다.
- 각 Task와 Phase Gate의 실행 명령, 기대 결과, 수동 확인 항목이 구체적이다.
- placeholder, 미정 값, 추정 계약이 없다.
- 변경 금지 항목과 중단 규칙이 명시돼 있다.

View File

@@ -9,9 +9,11 @@
## 2. 기준 문서와 템플릿
- 리뷰 전에 대상 기능 디렉터리의 `prd.md`, `api-contract.md`, `plan-task.md`와 관련 구현·test를 읽는다.
- 리뷰 문서는 대상 `prd.md`같은 디렉터리에 만든다.
- 대상 `prd.md``plan-task.md`가 있는 기능 문서 디렉터리 아래 `reviews/`를 만들고 모든 리뷰 문서를 그 안에 둔다.
- 리뷰 문서를 기능 문서 디렉터리 바로 아래나 단수형 `review/`에 두지 않는다. 여러 Phase·Task 리뷰가 생겨도 같은 `reviews/`에 누적한다.
- [코드 리뷰 보고서 샘플](../sample/sample-review.md)을 원본 템플릿으로 사용하고, section·필드·상태 의미를 임의로 축소하지 않는다.
- 실제 리뷰 문서 파일명은 범위가 드러나게 작성한다. 예: `review-phase-0-1.md`, `review-auth.md`.
- `prd.md``plan-task.md`에서 리뷰 문서를 참조할 때는 `./reviews/<리뷰 파일명>.md` 상대 링크를 사용한다.
## 3. 리뷰 수행 원칙

View File

@@ -1,6 +1,7 @@
# 실행 스크립트
- 개발 서버: `npm run dev` (`http://127.0.0.1:8888`)
- Mock preview 개발 서버: `npm run dev:mock` (`http://127.0.0.1:8889`)
- 개발 서버용 빌드: `npm run build:dev`
- 프로덕션 서버용 빌드: `npm run build:prod`
- 기본 프로덕션 빌드: `npm run build`
@@ -8,5 +9,7 @@
- 린트: `npm run lint`
- Vitest watch: `npm run test`
- Vitest 단발 실행: `npm run test:run`
- Playwright E2E: `npm run e2e`
- Playwright E2E(server mode): `npm run e2e``playwright.config.ts`의 server mode `testMatch`에 있는 spec만 실행하며, 추가 file filter를 넘기면 교집합만 실행한다.
- Playwright E2E(mock mode): `npm run e2e:mock``playwright.config.ts`의 mock mode `testMatch`에 있는 spec만 실행하며, 추가 file filter를 넘기면 교집합만 실행한다.
- Mock preview domain rule: 후속 도메인 Phase는 자기 handler, fixture, mock E2E를 같은 Phase에서 소유하고 추가한다.
- Phase 0 Gate 기준: `npm ci`, `npx playwright install chromium webkit`, `npm run typecheck`, `npm run lint`, `npm run test:run -- src/app/App.test.tsx src/shared/config/env.test.ts`, `npm run e2e -- tests/e2e/smoke.spec.ts`, `npm run build:dev`, `npm run build:prod`

View File

@@ -1,6 +1,6 @@
# Goal 실행형 구현 계획 샘플
> 이 문서는 goal 기능으로 구현 계획을 실행하기 위한 템플릿이다. 실제 `plan-task.md`를 만들 때 `<...>` placeholder를 모두 구체적인 값으로 교체한다. Phase는 결과와 의존성을 묶고, `create_goal`에는 Task 또는 Phase Gate 하나만 등록한다.
> 이 문서는 goal 기능으로 구현 계획을 실행하기 위한 템플릿이다. 실제 `plan-task.md`를 만들 때 `<...>` placeholder를 모두 구체적인 값으로 교체한다. Phase는 결과와 의존성을 묶고, `create_goal`에는 Task 또는 Phase Gate 하나만 등록한다. 각 구현 Task에는 RED/GREEN/REFACTOR 절차를, 테스트가 현실적으로 불가능한 Task에는 TDD 예외 사유와 대체 검증 방법을 적고, 모든 Task와 Phase Gate에 실행 명령·기대 결과·수동 확인을 둔다.
| 문서 항목 | 내용 |
|---|---|
@@ -49,7 +49,7 @@
- 의존성: 실제 소비 Task에서 필요한 최소 dependency만 추가한다.
- 계약: 제공되지 않은 endpoint, DTO, enum, 오류 status/key와 validation 상한을 추정하지 않는다.
- backend 구현 전 UI 확인이 필요하면 제공 계약 기반 explicit mock mode를 사용하고 실제 404 자동 fallback·production mock을 금지하며 mock/server 완료 증거를 분리한다.
- 구현: 모든 기능은 가장 작은 실패 test를 먼저 만들고 최소 구현으로 통과시킨다.
- 구현: 모든 기능은 `RED: 실패 테스트 작성/실패 확인``GREEN: 최소 구현/통과 확인``REFACTOR: 정리/회귀 확인` 순서로 진행한다. 실패 테스트가 현실적으로 불가능하면 Task에 TDD 예외 사유와 대체 검증 방법을 먼저 확정한다.
## Phase 1
@@ -80,11 +80,19 @@
- Consumes: `<선행 Task가 제공하는 type/function/component contract>`
- Produces: `<후속 Task가 사용할 정확한 type/function/component contract>`
- [ ] 가장 작은 실패 test를 작성한다.
- [ ] `<focused test 명령>`을 실행해 의도한 assertion 실패를 확인한다.
- [ ] test를 통과시키는 최소 구현을 작성한다.
- [ ] `<focused test 명령>`을 다시 실행해 성공을 확인한다.
- [ ] 관련 typecheck·lint를 실행하고 실제 결과를 Progress에 기록한다.
**TDD 절차:**
- [ ] **RED: 실패 테스트 작성/실패 확인**`<정확한 test 파일 경로>``<검증할 동작>`의 가장 작은 실패 test를 작성하고 `<focused test 명령>` 실행 시 `<의도한 assertion 메시지>`로 실패하는지 확인한다.
- [ ] **GREEN: 최소 구현/통과 확인**`<정확한 구현 파일 경로>`에 test를 통과시키는 최소 구현만 작성하고 같은 명령이 `exit 0`, `<N개 test 통과>`인지 확인한다.
- [ ] **REFACTOR: 정리/회귀 확인** — 중복·이름·구조만 정리한 뒤 `<focused test 명령>``<관련 회귀 test 명령>`이 모두 `exit 0`인지 확인한다.
**검증 기준:**
- **실행 명령:** `<focused test 명령>`, `<관련 회귀 test 명령>`, `<typecheck 명령>`, `<lint 명령>`
- **기대 결과:** 모든 명령 `exit 0`, `<focused N개·회귀 N개 test>` 통과, type·lint 오류 0건.
- **수동 확인:** `<viewport>`에서 `<사용자 동작>``<관찰 가능한 상태 변화>`가 발생하고 금지 동작은 발생하지 않는다.
- [ ] TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.
#### Task 1.2 `<두 번째 독립 결과>`
@@ -105,10 +113,19 @@
- Consumes: `<P1-T1이 제공한 정확한 contract>`
- Produces: `<Phase 2 또는 Gate가 사용할 정확한 contract>`
- [ ] 가장 작은 실패 test를 작성하고 의도한 실패를 확인한다.
- [ ] 최소 구현으로 focused test를 통과시킨다.
- [ ] 오류·loading·empty·success와 접근성 상태를 검증한다.
- [ ] 관련 test·typecheck·lint 결과를 Progress에 기록한다.
**TDD 절차:**
- [ ] **RED: 실패 테스트 작성/실패 확인**`<정확한 test 파일 경로>``<오류·loading·empty·success 중 이 Task가 소유한 상태>``<사용자 action>`의 실패 test를 작성하고 `<focused test 명령>`이 의도한 이유로 실패하는지 확인한다.
- [ ] **GREEN: 최소 구현/통과 확인** — 필요한 상태와 action만 최소 구현하고 같은 명령이 `exit 0`, `<N개 test 통과>`인지 확인한다.
- [ ] **REFACTOR: 정리/회귀 확인** — 상태 분기와 접근성 이름을 정리한 뒤 `<focused test 명령>``<P1-T1 관련 회귀 test 명령>`이 모두 통과하는지 확인한다.
**검증 기준:**
- **실행 명령:** `<focused UI test 명령>`, `<P1-T1 관련 회귀 test 명령>`, `<typecheck 명령>`, `<lint 명령>`
- **기대 결과:** 모든 명령 `exit 0`, `<상태·action별 N개 test>` 통과, type·lint 오류 0건.
- **수동 확인:** `<지원 viewport>`에서 오류·loading·empty·success 상태, keyboard focus 순서와 accessible name을 확인한다.
- [ ] TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.
### 완료 조건
@@ -126,6 +143,8 @@
- **완료 증거:** 아래 자동·수동 검증 통과와 Progress 기록.
- **범위 밖:** Gate 통과를 위한 test 삭제·완화와 관련 없는 기능 수정.
**실행 명령:**
```bash
<focused unit/integration test 명령>
<Phase 전용 E2E 명령>
@@ -134,9 +153,9 @@
<build 명령>
```
**Expected:** `<0 exit code, test 수, 사용자가 완료할 흐름, 금지 요청 0회 등 관찰 가능한 결과>`
**기대 결과:** `<모든 명령 exit 0, test 수, 사용자가 완료할 흐름, 금지 요청 0회 등 관찰 가능한 결과>`
수동 검증:
**수동 확인:**
- [ ] `<viewport와 사용자 흐름>`
- [ ] `<keyboard·focus·zoom·접근성 검사>`
@@ -160,11 +179,41 @@
- **완료 증거:** 계약 제공 또는 제외 결정이 기준 문서에 일치하고 구현 map이 기록됨.
- **범위 밖:** 계약을 추정한 production adapter와 실제 기능 구현.
**Files:**
- Modify: `<대상 prd.md 경로>`
- Modify: `<대상 api-contract.md 경로>`
- Modify: `<대상 plan-task.md 경로>`
- Test: 없음 — 이 Task의 산출물은 실행 코드가 아니라 확정된 계약과 구현 map이다.
**Interfaces:**
- Consumes: `<PRD 요구사항 ID와 외부에서 제공한 API 계약>`
- Produces: `<P2-T2가 사용할 endpoint·DTO·상태/action·component/file map>`
**TDD 예외 사유:** 이 Task는 실행 가능한 동작을 구현하지 않고 외부 근거로 계약과 책임 경계를 확정한다. 계약 확정 전에 실패 테스트를 만들면 제공되지 않은 endpoint·DTO를 추정하게 되므로 산출물을 올바르게 검증할 수 없다.
**대체 검증 방법:** PRD·API Contract·plan의 요구사항과 이름을 상호 대조하고, 금지된 미정 표현과 Markdown 변경 오류를 명령으로 검사한 뒤 문서의 추적성을 수동 확인한다.
- [ ] 필요한 endpoint·DTO·오류·pagination 계약을 확인한다.
- [ ] loading·empty·error·success·read-only·viewport 상태와 action을 inventory한다.
- [ ] 계약이 없으면 담당 주체·영향·재개 조건과 제외/후속 결정을 문서화한다.
- [ ] Page·feature·shared component와 test file 책임을 확정한다.
**검증 기준:**
- **실행 명령:**
```bash
! rg --pcre2 -n '^(?!\s*!?\s*rg\b).*(?:TBD|TODO|적절히 처리|나중에 구현|위와 동일)' <대상 prd.md 경로> <대상 api-contract.md 경로> <대상 plan-task.md 경로>
git diff --check -- <대상 prd.md 경로> <대상 api-contract.md 경로> <대상 plan-task.md 경로>
```
- **기대 결과:** 두 명령 모두 출력 없이 `exit 0`; 모든 요구사항 ID와 계약 이름이 세 문서에서 일치한다.
- **수동 확인:** endpoint·DTO·오류·pagination 및 상태/action 각각에 근거 또는 담당 주체·영향·재개 조건이 있고, P2-T2의 Files와 Interfaces가 구현 결정을 내릴 만큼 구체적이다.
- [ ] 대체 검증의 실제 결과를 Progress에 기록한다.
#### Task 2.2 `<Phase 2의 독립 구현 결과>`
**Goal 실행 `P2-T2`:** `<사용자가 직접 확인할 있는 흐름을 문장으로 작성한다.>`
@@ -179,11 +228,24 @@
- Modify: `<정확한 파일 경로>`
- Test: `<정확한 test 파일 경로>`
- [ ] contract와 serializer의 실패 test를 먼저 작성한다.
- [ ] UI 상태와 사용자 action의 실패 test를 먼저 작성한다.
- [ ] 최소 구현으로 focused test를 통과시킨다.
- [ ] 관련 integration/E2E와 공통 품질 명령을 실행한다.
- [ ] 실제 결과와 남은 항목을 Progress에 기록한다.
**Interfaces:**
- Consumes: `<P2-T1에서 확정한 endpoint·DTO·상태/action contract>`
- Produces: `<P2-GATE가 검증할 adapter·component·사용자 흐름 contract>`
**TDD 절차:**
- [ ] **RED: 실패 테스트 작성/실패 확인** — `<contract test 파일>`과 `<UI test 파일>`에 serializer, 상태와 사용자 action의 가장 작은 실패 test를 작성하고 `<focused test 명령>`이 `<의도한 실패 이유>`로 실패하는지 확인한다.
- [ ] **GREEN: 최소 구현/통과 확인** — P2-T1의 확정 계약만 사용하는 최소 adapter·UI를 구현하고 같은 명령이 `exit 0`, `<N개 test 통과>`인지 확인한다.
- [ ] **REFACTOR: 정리/회귀 확인** — contract 변환과 UI 상태 책임을 정리한 뒤 `<focused test 명령>`과 `<관련 integration/E2E 명령>`이 모두 통과하는지 확인한다.
**검증 기준:**
- **실행 명령:** `<focused contract/UI test 명령>`, `<관련 integration/E2E 명령>`, `<typecheck 명령>`, `<lint 명령>`, `<build 명령>`
- **기대 결과:** 모든 명령 `exit 0`, `<contract/UI/E2E별 N개 test>` 통과, type·lint·build 오류 0건, 금지된 request 0회.
- **수동 확인:** `<지원 viewport>`에서 success·loading·empty·error·read-only 흐름과 keyboard·focus 동작을 확인하고 실제 request payload가 API Contract와 일치한다.
- [ ] TDD 단계와 검증 기준의 실제 결과 및 남은 항목을 Progress에 기록한다.
### 완료 조건
@@ -198,16 +260,24 @@
**Goal 실행 `P2-GATE`:** Phase 2의 contract, 사용자 흐름과 회귀 방지를 최종 판정한다.
- **시작 조건:** Phase 2의 모든 활성 Task goal 완료.
- **완료 증거:** 아래 명령과 Expected 통과, Progress에 실제 결과 누적.
- **완료 증거:** 아래 실행 명령, 기대 결과와 수동 확인을 모두 통과하고 Progress에 실제 결과 누적.
- **범위 밖:** 실패와 무관한 다음 Phase 구현.
**실행 명령:**
```bash
<Phase 2 focused test 명령>
<Phase 2 E2E 명령>
<typecheck·lint·build 명령>
```
**Expected:** `<사용자 journey, 오류 처리, request payload와 금지 동작을 포함한 최종 결과>`
**기대 결과:** `<모든 명령 exit 0, test , 사용자 journey, 오류 처리, request payload와 금지 동작을 포함한 최종 결과>`
**수동 확인:**
- [ ] `<지원 viewport에서 success·loading·empty·error·read-only 흐름>`
- [ ] `<keyboard·focus·zoom·접근성 동작>`
- [ ] `<API Contract와 실제 request·response production mock 미사용>`
## 실행 순서와 의존성
@@ -252,6 +322,11 @@ P1-T1 → P1-T2 → P1-GATE → P2-T1 → P2-T2 → P2-GATE
- 상태: 진행 중 / 완료 / 차단 감사 중 / 차단
- 무엇을: `<이번 실행에서 완료한 체크박스와 산출물>`
- 왜: `<Task objective와 요구사항 근거>`
- TDD: `<구현 Task는 RED/GREEN/REFACTOR만, 예외 Task는 예외 항목만 남긴다.>`
- RED: `<실패 test 명령>` — `<의도한 실패, exit code와 assertion>`
- GREEN: `<같은 focused test 명령>` — `<성공, exit code와 test >`
- REFACTOR: `<focused·관련 회귀 test 명령>` — `<성공/실패, exit code와 test >`
- 예외 Task: `<TDD 예외 사유와 대체 검증 결과. 구현 Task에서는 행을 삭제한다.>`
- 어떻게:
- `<실행 명령>` — `<성공/실패, exit code, test 수와 핵심 결과>`
- `<수동 검증>` — `<성공/실패/불가 사유>`
@@ -276,7 +351,7 @@ P1-T1 → P1-T2 → P1-GATE → P2-T1 → P2-T2 → P2-GATE
- 구현 중 발견한 범위 내 문제는 근거와 재현 방법을 기록하고 해당 Task에서 처리한다.
- 완료된 범위의 회귀는 기존 Task를 다시 열지 않고 별도 회귀 수정 Task와 goal을 만든다.
- 범위 밖 문제는 임의로 수정하지 않고 사용자에게 보고하거나 후속 Task로 결정한다.
- 상세 코드 리뷰 결과가 필요하면 `sample-review.md` 형식의 별도 review 문서를 사용한다.
- 상세 코드 리뷰 결과가 필요하면 기능 문서 디렉터리의 `reviews/` 아래에 `sample-review.md` 형식의 별도 review 문서를 만든다.
## 최종 보고 형식

View File

@@ -1,6 +1,6 @@
# 코드 리뷰 보고서 샘플
> 이 문서는 완료된 Phase를 다시 검토할 때 사용하는 템플릿이다. 리뷰에서 발견한 후보를 먼저 검증하고, **확정**된 항목만 `plan-task.md`의 회귀 수정 Task와 goal로 전환한다. 기존 완료 체크박스와 검증 기록은 삭제하거나 되돌리지 않는다.
> 이 문서는 완료된 Phase를 다시 검토할 때 사용하는 템플릿이다. 실제 리뷰 문서는 대상 `prd.md`·`plan-task.md` 디렉터리 아래 `reviews/`에 둔다. 리뷰에서 발견한 후보를 먼저 검증하고, **확정**된 항목만 `plan-task.md`의 회귀 수정 Task와 goal로 전환한다. 기존 완료 체크박스와 검증 기록은 삭제하거나 되돌리지 않는다.
## 1. 리뷰 정보