Files

20 KiB

Phase 3 Character Workspace 코드 리뷰·QA

1. 리뷰 정보

항목 내용
리뷰 대상 Phase 3 / Character 목록·상세·편집·워크스페이스
기준 commit 또는 working tree dd30e36323543e8f60e9983326503653e8001f12, 2026-07-31 종합 재점검 당시 사용자 변경을 포함한 current working tree
리뷰 일자 2026-07-31
리뷰어 Codex
기준 문서 prd.md, api-contract.openapi.json 2.3.0, plan-task.md
리뷰 상태 판정 완료, 회귀 수정 완료

2. 리뷰 목적과 범위

목적

  • Character CRUD, URL 문맥, 비활성 read-only 정책과 탭 접근성이 요구사항·계약에 맞는지 확인한다.
  • 데스크톱·모바일·keyboard-only 워크스페이스 회귀 범위를 점검한다.

포함 범위

  • 코드: src/features/characters, src/layouts/CharacterWorkspaceLayout.tsx
  • 테스트: Character unit/integration 및 character-workspace.spec.ts
  • 문서: Character 요구사항, PRD §7.2·§14.2, P3-*
  • 수동 검증: WAI-ARIA Tabs Pattern과 markup·keyboard handler 정적 대조

제외 범위

  • Phase 4~8 하위 리소스의 도메인별 mutation
  • 실제 외부 서버 Character fixture를 이용한 수동 QA

3. 판정 기준

심각도 기준
Blocker 핵심 Character 관리 불능 또는 데이터·보안 위험
High Character 요구사항·계약의 주요 위반
Medium 특정 입력 방식·화면에서 기능 또는 접근성이 깨짐
Low 비핵심 UX·운영 문구·유지보수 정합성 문제

상태는 후보, 확정, 오탐, 보류, 수정 완료를 사용한다.

4. 검토한 근거

문서와 코드

  • 요구사항: Character 요구사항, PRD §7.2, §10.3, §14.2
  • 계획: P3-T1~P3-GATE
  • 코드: src/layouts/CharacterWorkspaceLayout.tsx:50-63
  • 테스트: src/layouts/CharacterWorkspaceLayout.test.tsx:89-105, tests/e2e/character-workspace.spec.ts
  • 외부 기준: WAI-ARIA Authoring Practices Guide Tabs Pattern

실행 환경

macOS 26.0 / Node v24.12.0 / npm 11.7.0
Playwright 4 projects, 320/768/1280px와 200% zoom 자동화 포함

실행한 검증

명령 또는 수동 검증 결과 핵심 증거
npm run test:run 성공 전체 72 files, 354 tests passed
npm run e2e:mock 부분 실패 Character workspace 관련 실행 항목은 통과 또는 프로젝트 정책상 skip; 전체 실패는 Phase 0 origin 4건과 비재현 Series timeout
npm run typecheck / npm run lint / npm run build 성공 모두 exit 0
tab markup 정적 검증 실패 5개 role=tab이 모두 기본 tab stop이며 ArrowLeft/ArrowRight·roving tabindex 없음
비활성 안내 문구 정적 검증 실패 사용자 화면과 test에 내부 Task ID P3-T2 노출

5. 발견 사항 요약

ID 심각도 상태 제목 소유 Task 후속 goal
REV-P3-004 Medium 수정 완료 ARIA tablist가 화살표 키·roving tabindex·tabpanel 이름 연결을 제공하지 않는다 P3-R3 P3-R3
REV-P3-005 Low 수정 완료 비활성 캐릭터 안내에 내부 계획 ID와 오래된 범위 문구가 노출된다 P3-R3 P3-R3

6. 발견 사항 상세

REV-P3-004 — ARIA tablist가 화살표 키·roving tabindex·tabpanel 이름 연결을 제공하지 않는다

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §7.2, §10.3, §14.2
  • 관련 계약: 없음
  • 소유 Task: 신규 P3-R3

관찰 내용

워크스페이스는 링크 5개를 role="tab"으로 선언하지만 모두 기본 tab stop이다. 선택 tab만 tabIndex=0으로 두는 roving tabindex, 좌우 화살표 이동, Home/End 처리와 tabpanelaria-labelledby 연결이 없다.

근거

  • 코드: src/layouts/CharacterWorkspaceLayout.tsx:56-63
  • 테스트: tabs의 선택 상태는 확인하지만 화살표 이동·roving tabindex·tabpanel accessible name 회귀 test가 없음
  • 문서: PRD §14.2는 keyboard-only 탭 이동을 성공 기준으로 둠
  • 표준: WAI-ARIA Tabs Pattern은 horizontal tablist에서 좌우 화살표 이동과 활성 tab 하나의 tab sequence 진입을 정의함

재현 또는 검증 절차

  1. /ai-characters/:characterId에서 키보드로 워크스페이스 tablist에 진입한다.
  2. ArrowRight 또는 ArrowLeft를 누른다.
  3. 선택·focus가 다음/이전 tab으로 이동하지 않는 것을 확인한다.
  4. Tab을 반복하면 모든 tab을 개별 순회하며 tabpanel이 tab label로 명명되지 않는 것을 확인한다.

영향

키보드와 보조기술 사용자는 ARIA tab widget의 표준 동작을 사용할 수 없고, five-tab navigation에 불필요한 tab stop을 반복한다.

권장 조치

기존 link navigation을 유지하면서 roving tabindex, 방향키/Home/End, tab ID와 aria-labelledby 연결을 추가하고 component/E2E keyboard 회귀 test를 작성한다.

판정 기록

  • 2026-07-30 — PRD keyboard 기준, APG pattern과 현재 markup·handler를 대조해 확정.
  • 2026-07-30 — P3-R3에서 link navigation 의미를 nav로 정정하고 현재 위치·keyboard focus 회귀 test를 추가해 수정 완료 판정.

REV-P3-005 — 비활성 캐릭터 안내에 내부 계획 ID와 오래된 범위 문구가 노출된다

  • 심각도: Low
  • 상태: 수정 완료
  • 관련 요구사항: 비활성 Character workspace read-only 정책
  • 관련 계약: 없음
  • 소유 Task: 신규 P3-R3

관찰 내용

비활성 banner가 “P3-T2 범위에는 ... 진입점이 없습니다”라고 표시한다. 현재는 후속 Phase에서 mutation이 구현됐고 공통 정책으로 차단되므로 내부 Task ID와 과거 범위 설명 모두 사용자 관점에서 부정확하다.

근거

  • 코드: src/layouts/CharacterWorkspaceLayout.tsx:50-54
  • 테스트: src/layouts/CharacterWorkspaceLayout.test.tsx:103이 내부 문구를 고정
  • 문서: PRD §12는 비활성 workspace의 mutation 진입점 차단이라는 제품 정책만 규정

재현 또는 검증 절차

  1. 비활성 Character 상세에 진입한다.
  2. read-only banner를 읽는다.
  3. 사용자에게 의미 없는 계획 ID P3-T2와 현재 구현 범위에 맞지 않는 문구가 노출되는 것을 확인한다.

영향

운영자에게 내부 개발 정보를 노출하고 실제 read-only 정책의 이유와 범위를 혼동시킨다.

권장 조치

Task ID를 제거하고 “비활성 캐릭터에서는 생성·수정·삭제할 수 없다”처럼 현재 정책을 설명하는 운영 문구로 교체한다.

판정 기록

  • 2026-07-30 — 현재 후속 Phase 구현과 고정 test 문구를 대조해 확정.
  • 2026-07-30 — P3-R3에서 내부 Task ID를 제거하고 현재 read-only 정책 문구로 교체해 수정 완료 판정.

7. 확정 항목의 plan·goal 전환

  • REV-P3-004, REV-P3-005plan-task.md 신규 P3-R3
  • goal objective: [P3-R3] 워크스페이스 tab keyboard semantics와 비활성 운영 문구를 복구한다.

8. 리뷰 종료 판정

판정 항목 결과 근거
리뷰 범위 전체 확인 충족 Character 코드·test·접근성 기준 대조
후보 항목 판정 완료 충족 2건 수정 완료
확정 항목 plan 반영 충족 P3-R3 완료
보류 항목의 담당·재개 조건 기록 해당 없음 보류 없음
검증 명령과 결과 기록 충족 §4

최종 결론: REV-P3-004, REV-P3-005 수정 완료

남은 항목: Phase 3 자체 회귀 없음. 공통 비활성화 mutation pending·오류 복구는 REV-P9-004/P9-R4에서 추적한다.

9. 수정 후 검증 기록

P3-R3 수정 후 검증 — 2026-07-30

  • RED: npm run test:run -- src/layouts/CharacterWorkspaceLayout.test.tsx는 기존 tab semantics와 내부 Task 문구 assertion에서 2 failed / 3 passed였다.
  • GREEN: link 기반 화면 이동은 native nav semantics와 aria-current=page로 정렬하고, 비활성 안내에서 P3-T2를 제거했다. focused layout test는 5 passed였다.
  • 회귀: 관련 9 files / 45 tests, Chromium Character E2E 11 tests가 통과했고 npm run typecheck, npm run lint는 exit 0이었다.
  • 2차 전체 검증: npm run test:run 72 files / 358 tests, npm run build 255 modules transformed, server allowlist E2E 36 passed로 현재 수정 상태를 재확인했다.

10. 2026-07-31 재점검

실행·판정 요약

  • 기준: commit dd30e36323543e8f60e9983326503653e8001f12, PRD §7.1~7.2·CHAR-001~018·FILE-010과 OpenAPI Character operation 재대조.
  • 검증: 전체 unit 72 files / 360 tests, server allowlist 36 tests, typecheck·lint·build가 통과했다.
  • 신규 발견: High 1건, Medium 2건. 기존 REV-P3-004~005는 수정 완료 상태를 유지한다.
ID 심각도 상태 제목 소유 Task 후속 goal
REV-P3-006 Medium 수정 완료 워크스페이스 header가 필수 characterId를 숨긴다 P3-R4 P3-R4
REV-P3-007 Medium 수정 완료 Character URL이 계약 query searchTerm 대신 search를 사용한다 P3-R5 P3-R5
REV-P3-008 High 수정 완료 Character 원본 image를 crop 적용 전에 제출할 수 있다 P3-R6 P3-R6

REV-P3-006 — 워크스페이스 header가 필수 characterId를 숨긴다

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §7.2
  • 소유 Task: 신규 P3-R4

관찰 내용

수정 전 공통 workspace header는 이미지·이름·상태만 표시하며 character.id를 렌더링하지 않았다. 테스트는 요구사항과 반대로 characterId: 미노출을 성공 조건으로 고정했다. 프로필 본문의 characterUUID는 numeric characterId를 대체하지 않는다.

근거

  • 문서: prd.md:155-159 — 상단에 characterId를 항상 표시
  • 코드: src/layouts/CharacterWorkspaceLayout.tsx:40-55
  • 테스트: src/layouts/CharacterWorkspaceLayout.test.tsx:81-85
  • 이력: plan-task.md의 2026-07-30 visual QA 기록이 header ID 제거를 명시한다.

재현 또는 검증 절차

  1. /ai-characters/101 또는 하위 workspace route에 진입한다.
  2. 공통 header에서 이미지·루나·공개 상태는 보이지만 numeric 101 식별자는 없음을 확인한다.

영향 및 권장 수정 방향

이름이 같거나 유사한 캐릭터를 운영할 때 현재 작업 대상을 오인할 수 있다. 공통 header에 label과 함께 numeric ID를 복구하고 active/inactive 및 모든 하위 route 회귀 test를 추가한다.

판정 기록:

  • 2026-07-31 — PRD 명시 요구사항과 구현·반대 assertion을 대조해 확정.
  • 2026-07-31 — P3-R4에서 active/inactive header에 numeric characterId를 복구하고 layout·Character focused unit으로 수정 완료를 확인했다.

REV-P3-007 — Character URL이 계약 query searchTerm 대신 search를 사용한다

  • 심각도: Medium
  • 상태: 수정 완료
  • 관련 요구사항: PRD §7.1
  • 관련 계약: GET /api/v2/admin/ai-characters query searchTerm, page, size
  • 소유 Task: 신규 P3-R5

관찰 내용

수정 전 API adapter에는 searchTerm을 전달하지만 브라우저 URL parser와 serializer는 별도 search key를 사용했다. 따라서 계약 이름의 직접 링크 ?searchTerm=루나는 검색 상태로 복원되지 않고 기존 테스트도 ?search=루나를 정상 규칙으로 승인했다.

근거

  • 문서: prd.md:147-150
  • 코드: src/features/characters/pages/CharacterListPage.tsx:26-43,55
  • 테스트: src/features/characters/tests/character-list.test.tsx:50-66,103

재현 또는 검증 절차

  1. /ai-characters?searchTerm=루나&page=1&size=20으로 직접 진입한다.
  2. 검색 input이 빈 값이고 API request의 searchTerm이 복원되지 않는지 확인한다.
  3. UI에서 검색하면 URL이 search=...로 생성되는지 확인한다.

영향 및 권장 수정 방향

공유·새로고침 URL과 계약 추적성이 어긋나며 외부에서 생성한 정상 계약 deep link가 무시된다. URL read/write key만 searchTerm으로 맞추고 직접 진입·pagination 왕복 test를 추가한다.

판정 기록:

  • 2026-07-31 — PRD·OpenAPI query 계약과 구현·테스트의 search URL key를 대조해 확정.
  • 2026-07-31 — P3-R5에서 Character URL read/write key를 searchTerm으로 정렬하고 Character focused unit·정적 Gate로 수정 완료를 확인했다.

판정 기록: 2026-07-31 — PRD·OpenAPI·URL code/test를 대조해 확정.

REV-P3-008 — Character 원본 image를 crop 적용 전에 제출할 수 있다

  • 심각도: High
  • 상태: 수정 완료
  • 관련 요구사항: FILE-007, FILE-009~010, PRD §10.5
  • 관련 계약: Character create 필수 image, update optional image multipart part
  • 소유 Task: 신규 P3-R6

관찰 내용

생성·수정의 selectImage는 파일 선택 즉시 raw File을 committed image state에 넣은 뒤 crop source를 비동기로 준비한다. 생성 form은 준비·dialog 상태를 submit guard에 포함하지 않고 crop 취소도 raw image를 지우지 않는다. 따라서 원본 제출 또는 취소한 파일 제출이 가능하다. 수정도 source 준비가 끝나기 전 raw replacement를 보낼 수 있고 준비 reject·연속 선택의 stale resolution을 처리하지 않는다.

근거

  • 문서: prd.md:346-361
  • 코드: src/features/characters/pages/CharacterCreatePage.tsx:63-92,103-127,165-180
  • 코드: src/features/characters/pages/CharacterEditPage.tsx:78-109,120-145,184-205
  • 테스트가 승인하는 회귀: src/features/characters/tests/CharacterCreatePage.test.tsx:147-170은 crop 적용 없이 선택한 raw file이 multipart에 포함되는 것을 기대한다.

재현 또는 검증 절차

  1. source 준비 Promise를 pending으로 둔 채 Character 생성 필수 text를 채우고 valid PNG를 선택한다.
  2. 생성을 누르면 crop 적용 전 raw PNG가 request에 포함될 수 있다.
  3. source 준비 뒤 crop dialog에서 취소하고 생성해도 같은 raw file이 남는다.
  4. 수정 화면에서도 느린 첫 선택과 빠른 둘째 선택을 교차 resolve하면 마지막 선택이 아닌 dialog가 열릴 수 있다.

영향

1:1·최대 800px 보장을 거치지 않은 원본이나 사용자가 취소한 파일이 서버에 저장될 수 있어 media 무결성과 운영자 의도를 직접 위반한다.

권장 수정 방향

raw selection과 crop 적용 결과를 분리하고 마지막 selection token, 준비 상태, reject 오류, 준비/dialog 중 submit guard를 둔다. create 취소는 새 선택을 제거하고 edit 취소는 기존 서버 media를 유지해야 한다.

판정 기록:

  • 2026-07-31 — PRD file 정책, 비동기 state 전이, 현재 통과 test를 함께 대조해 확정.
  • 2026-07-31 — P3-R6에서 create/edit committed image를 crop apply 결과로 한정하고 pending/cancel/stale/reject lifecycle test를 추가했다. npm run test:run -- src/features/characters는 6 files / 43 tests passed이고 npm run typecheck, npm run lint, npm run build, git diff --check는 모두 성공했다. E2E는 사용자 지시에 따라 최종 회귀 단계로 이연해 수정 완료 판정.

plan·goal 전환 및 종료 판정

  • REV-P3-006P3-R4
  • REV-P3-007P3-R5
  • REV-P3-008P3-R6
  • 최종 결론: High 1건·Medium 2건 모두 수정 완료. Blocker 없음.
  • 교차 QA: 당시 Mobile Safari dirty-leave 실패는 focused 1회와 --repeat-each=5에서 모두 통과했고, 이후 지원 project 축소로 현재 Task에서 제외했다.

11. 최종 Phase별 점검 — 2026-07-31

  • 검토 범위: Character 목록 query, 상세 workspace, create/update/deactivate payload, inactive read-only, image 정책·crop lifecycle을 CHAR-*, OpenAPI, P3와 대조했다.
  • 실행 증거: npm run test:run -- src/features/characters 6 files / 43 tests 통과. mock Chromium의 Character workspace·교차 resource·접근성 흐름도 전체 57 tests 안에서 통과했다.
  • 판정: REV-P3-006~008 수정 완료 상태가 유지되며 Phase 3 확정 신규 발견 사항 없음.
  • 남은 위험: 실제 개발 API Character mutation 수동 QA는 별도 Gate 상태다.
  • 신규 Task: 없음.

12. 종합 재점검 — 2026-07-31

  • 검토 범위: Character 목록·상세·생성·수정·비활성화, original work lookup, dirty-leave와 image 적용 lifecycle을 PRD CHAR-*·OpenAPI·현재 test에 재대조했다.
  • 실행 증거: Character를 포함한 도메인 묶음 36 files / 206 tests passed. 현재 mock Chromium·Mobile Chrome 전체 Gate 109 passed / 5 skipped에도 Character 흐름이 포함됐다.
  • 판정: Character endpoint·DTO·화면 소유의 확정 신규 발견 사항 없음.
  • 교차 Phase: Character도 영향을 받는 crop frame/좌표와 Blob URL 문제는 공용 컴포넌트 소유 REV-P1-016~017/P1-R10~R11에서 수정한다.
  • 남은 위험: 실제 개발 API Character mutation 수동 QA와 P9-R7의 Safari dirty-leave 재검증이 필요하다.
  • 신규 Phase 3 Task: 없음.

13. 요청 기준 재리뷰 — 2026-07-31

  • 검토 범위: Character list/search/detail/create/update/deactivate, original work lookup, optional field serialization, crop lifecycle와 workspace capability.
  • 실행 증거: 관련 unit은 전체 실행에서 통과했고 mock 4-project matrix의 Character journey·axe·zoom·read-only 흐름도 0 failure였다. OpenAPI 2.3.0 parse·schema ref 점검도 통과했다.
  • 판정: Phase 3 소유의 확정 신규 발견 사항 없음.
  • 남은 위험: 실제 개발 API Character mutation·active-only 결과와 실기기 Safari QA는 자동 mock 검증과 분리된 수동 QA다.
  • 신규 Phase 3 Task: 없음.

14. 최종 재검증 — 2026-07-31

  • 검토 범위: Character list/search/detail/create/update/deactivate, original work lookup, optional field serialization, image lifecycle와 workspace capability.
  • 실행 증거: 전체 unit 394 tests와 4-project mock matrix의 Character·axe·320px/200% zoom 시나리오가 0 failure였고 OpenAPI schema ref 검사도 통과했다.
  • 판정: Phase 3 소유의 확정 신규 발견 사항 없음.
  • 남은 위험: 실제 개발 API Character mutation·active-only 결과와 실기기 Safari QA.
  • 신규 Phase 3 Task: 없음.

15. 2026-07-31 문서 기준 재리뷰

  • 검토 범위: Character list/search/detail/create/update/deactivate, original work lookup, optional field serialization, inactive read-only과 image lifecycle를 CHAR-001~018·OpenAPI·P3에 대조했다.
  • 실행 증거: Character workspace·mutation을 포함한 실패 후보 5-spec focused는 5 files / 25 tests passed, server E2E는 36 passed, type·lint·build는 exit 0이었다. full unit 5 failure는 REV-P9-009/P9-R9로, WebKit direct-route navigation 경합은 동일 focused 통과 후 REV-P9-010/P9-R10으로, Character/Audio 이미지에도 영향을 주는 공용 crop frame은 REV-P1-019/P1-R13으로 분리했다.
  • 판정: Character endpoint·DTO·workspace 소유의 확정 신규 발견 사항 없음. 후보·오탐·보류 0건, 신규 Phase 3 Task 없음.
  • 남은 위험: 실제 Character mutation·active-only 수동 QA와 공용 P1-R13~R15, P9-R9~R10 완료 후 회귀가 필요하다.