190 lines
9.9 KiB
Markdown
190 lines
9.9 KiB
Markdown
# Phase 1 코드 리뷰·QA — 2026-07-29
|
||
|
||
## 1. 리뷰 정보
|
||
|
||
| 항목 | 내용 |
|
||
|---|---|
|
||
| 리뷰 대상 | Phase 1 / 공통 API·인증·file/crop 기반 |
|
||
| 기준 commit 또는 working tree | `dd30e36323543e8f60e9983326503653e8001f12`; 리뷰 시작 시 tracked diff 36개, 전체 변경 169개 |
|
||
| 리뷰 일자 | 2026-07-29 |
|
||
| 리뷰어 | Codex |
|
||
| 기준 문서 | `prd.md`, `api-contract.openapi.json`, `plan-task.md` |
|
||
| 리뷰 상태 | 판정 완료 |
|
||
|
||
## 2. 리뷰 목적과 범위
|
||
|
||
### 목적
|
||
|
||
- `AUTH-001~013`, `FILE-001~015`, 공통 API envelope와 crop 계약을 현재 구현과 대조한다.
|
||
- 기존 `review-phase-0-1.md`에서 수정 완료된 항목은 보존하고 새 회귀만 판정한다.
|
||
|
||
### 포함 범위
|
||
|
||
- 코드: `src/shared/api`, `src/shared/lib/crop-image.ts`, `src/shared/ui/image-crop-dialog.tsx`, 인증 저장소
|
||
- 테스트: 공통 API 인증·오류, crop 계산·Dialog
|
||
- 문서: Phase 1 Task·Gate와 PRD 인증/file 기준
|
||
- 수동 검증: 코드 계산과 keyboard/pointer control 정적 대조
|
||
|
||
### 제외 범위
|
||
|
||
- resource별 form과 multipart serializer, 실제 모바일 pinch 기기 QA
|
||
|
||
## 3. 판정 기준
|
||
|
||
| 심각도 | 기준 |
|
||
|---|---|
|
||
| Blocker | 보안·데이터 손실 또는 핵심 흐름 불능 |
|
||
| High | 확정 요구사항·계약 위반 또는 주요 회귀 |
|
||
| Medium | 제한 조건의 기능·접근성·복구 문제 |
|
||
| Low | 비핵심 UX·문서 정합성 |
|
||
|
||
상태는 `확정/오탐/보류/수정 완료`를 사용했다.
|
||
|
||
## 4. 검토한 근거
|
||
|
||
### 문서와 코드
|
||
|
||
- 요구사항: `AUTH-005`, `FILE-012`, PRD `10.4`
|
||
- 코드: `src/shared/api/client.ts:92`, `src/shared/api/client.ts:102`, `src/shared/api/client.ts:122`, `src/shared/lib/crop-image.ts:67`, `src/shared/lib/crop-image.ts:69`, `src/shared/ui/image-crop-dialog.tsx:38`, `src/shared/ui/image-crop-dialog.tsx:154`
|
||
- 테스트: `src/shared/api/__tests__/client-auth.test.ts`, `src/shared/lib/crop-image.test.ts`, `src/shared/ui/__tests__/image-crop-dialog.test.tsx`
|
||
|
||
### 실행 환경
|
||
|
||
```text
|
||
OS: macOS 26.0
|
||
Node: v24.12.0
|
||
npm: 11.7.0
|
||
API mode: unit stub/MSW
|
||
```
|
||
|
||
### 실행한 검증
|
||
|
||
| 명령 또는 검증 | 결과 | 핵심 증거 |
|
||
|---|---|---|
|
||
| `npm run test:run -- src/shared/lib/crop-image.test.ts src/shared/ui/__tests__/image-crop-dialog.test.tsx src/shared/api/__tests__/client-auth.test.ts src/shared/api/__tests__/client.test.ts` | 성공 | 4 files / 27 tests passed |
|
||
| `node` crop 계산 | 실패 재현 | 600px 원본, zoom 1.5에서 source crop 400px, output 600px, 1.5배 확대 |
|
||
| 코드 경로 대조 | 실패 재현 | 401 처리가 정상 오류 envelope parse 뒤에만 존재 |
|
||
|
||
## 5. 발견 사항 요약
|
||
|
||
| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 goal |
|
||
|---|---|---|---|---|---|
|
||
| `REV-P1-011` | High | 수정 완료 | 비정상 envelope·JSON인 401은 session을 제거하지 않는다 | `P1-T2` | `P1-R6` |
|
||
| `REV-P1-012` | High | 수정 완료 | zoom crop이 선택 영역보다 큰 결과를 만들어 no-upscale을 위반한다 | `P1-T5` | `P1-R7` |
|
||
| `REV-P1-013` | Medium | 수정 완료 | crop Dialog에 pinch/zoom 입력이 없다 | `P1-T5` | `P1-R7` |
|
||
|
||
## 6. 발견 사항 상세
|
||
|
||
### REV-P1-011 — 비정상 401에서 인증 만료 처리가 누락된다
|
||
|
||
- **심각도:** High
|
||
- **상태:** 수정 완료
|
||
- **관련 요구사항:** `AUTH-005`
|
||
- **관련 계약:** 보호 요청의 HTTP 401
|
||
- **소유 Task:** 신규 `P1-R6`
|
||
|
||
**관찰 내용**
|
||
|
||
`response.json()` 실패와 envelope schema 실패는 즉시 공통 `ApiError`를 던진다. session clear와 login 이동은 정상 오류 envelope가 parse된 뒤의 `!apiResponse.success` 분기에서만 실행된다.
|
||
|
||
**재현 또는 검증 절차**
|
||
|
||
1. 인증이 필요한 요청에 session을 저장한다.
|
||
2. server가 body 없는 401, non-JSON 401 또는 malformed envelope 401을 반환하게 한다.
|
||
3. 현재 결과는 status 401 오류만 발생하고 session clear/redirect callback은 0회다.
|
||
4. 요구 결과는 body 형태와 무관하게 보호 요청 401에서 1회 session clear와 login 이동이다.
|
||
|
||
**영향**
|
||
|
||
만료·무효 token이 브라우저 session에 남고 사용자가 보호 화면에서 반복 실패할 수 있다.
|
||
|
||
**권장 조치**
|
||
|
||
응답 body parse 전에 HTTP status 기반 인증 만료를 공통 처리하되 동시 401 burst 1회 규칙을 유지하고 malformed/empty 401 회귀 test를 추가한다.
|
||
|
||
**판정 기록**
|
||
|
||
- 2026-07-29 — 코드의 모든 401 경로를 대조해 확정.
|
||
- 2026-07-30 — `P1-R6`에서 HTTP status 기반 인증 만료 처리를 body parse 전에 수행하도록 수정하고 malformed JSON, empty body, schema mismatch 401 회귀를 추가해 수정 완료로 판정.
|
||
|
||
### REV-P1-012 — zoom crop 결과가 선택 원본 영역을 확대한다
|
||
|
||
- **심각도:** High
|
||
- **상태:** 수정 완료
|
||
- **관련 요구사항:** `FILE-012`
|
||
- **관련 계약:** resource별 `noUpscale=true`
|
||
- **소유 Task:** 신규 `P1-R7`
|
||
|
||
**관찰 내용**
|
||
|
||
출력 크기는 항상 `zoom=1`인 crop 영역으로 계산하지만 실제 source 영역은 현재 zoom으로 축소된다. 600×600 원본을 1.5배 확대하면 source는 400×400인데 output은 600×600이다.
|
||
|
||
**재현 또는 검증 절차**
|
||
|
||
1. 600×600 이미지와 1:1, maxWidth 800, noUpscale 정책을 연다.
|
||
2. zoom을 1.5로 바꾸고 적용한다.
|
||
3. `calculateCropSourceRect`는 400×400, Dialog는 output 600×600을 전달한다.
|
||
4. 요구 결과는 output이 선택 source crop의 pixel 크기를 넘지 않는 것이다.
|
||
|
||
**영향**
|
||
|
||
Character·Audio·Series·Community JPEG/PNG crop이 확대되어 품질이 저하되고 파일 계약을 위반한다.
|
||
|
||
**권장 조치**
|
||
|
||
output size 계산에 현재 zoom의 source rect를 사용하고 zoom 경계별 실제 canvas draw test를 추가한다.
|
||
|
||
**판정 기록**
|
||
|
||
- 2026-07-29 — 계산 결과 `scale=1.5`로 확정.
|
||
- 2026-07-30 — `P1-R7`에서 `CropOutputSizeRequest.zoom`을 반영해 zoom 1.5의 600×600 source crop output을 400×400으로 제한했고, focused/broad Vitest와 typecheck로 수정 완료 판정.
|
||
|
||
### REV-P1-013 — crop Dialog가 pinch/zoom을 지원하지 않는다
|
||
|
||
- **심각도:** Medium
|
||
- **상태:** 수정 완료
|
||
- **관련 요구사항:** PRD `10.4`
|
||
- **관련 계약:** 없음
|
||
- **소유 Task:** 신규 `P1-R7`
|
||
|
||
**관찰 내용**
|
||
|
||
range, 버튼/keyboard zoom과 단일 pointer drag는 있으나 두 pointer 거리 또는 gesture를 처리하는 pinch 입력 경로가 없다.
|
||
|
||
**영향**
|
||
|
||
tablet/mobile touch 사용자가 명시된 pinch/zoom 방식으로 crop을 조작할 수 없다.
|
||
|
||
**권장 조치**
|
||
|
||
기존 range/keyboard 대안을 유지한 채 두 pointer pinch를 최소 구현하고 pointer 회귀 test와 실제 touch QA 항목을 추가한다.
|
||
|
||
**판정 기록**
|
||
|
||
- 2026-07-29 — pointer handler와 테스트 전체 검색에서 pinch 경로 0건으로 확정.
|
||
- 2026-07-30 — `P1-R7`에서 native pointer map/ref 기반 two-pointer pinch zoom을 추가하고 pointerup/pointercancel/pointerleave cleanup을 연결했다. Dialog 회귀 test로 pinch zoom 1.5, 기존 drag/range/keyboard 경로를 확인해 수정 완료 판정.
|
||
|
||
## 7. 확정 항목의 plan·goal 전환
|
||
|
||
- `REV-P1-011` → `P1-R6`
|
||
- `REV-P1-012~013` → `P1-R7`
|
||
|
||
## 8. 리뷰 종료 판정
|
||
|
||
| 판정 항목 | 결과 | 근거 |
|
||
|---|---|---|
|
||
| 리뷰 범위 전체 확인 | 충족 | API 401·crop code/test 대조 |
|
||
| 후보 항목 판정 완료 | 충족 | 3건 모두 확정 |
|
||
| 확정 항목 plan 반영 | 충족 | `P1-R6`, `P1-R7` |
|
||
| 보류 항목 담당·재개 조건 | 해당 없음 | 보류 없음 |
|
||
| 검증 명령과 결과 기록 | 충족 | §4 |
|
||
|
||
**최종 결론:** 확정 항목 수정 완료.
|
||
|
||
**남은 항목:** 없음.
|
||
|
||
## 9. 수정 후 검증 기록
|
||
|
||
- 2026-07-30 `P1-R6` — RED `npm run test:run -- src/shared/api/__tests__/client-auth.test.ts src/shared/api/__tests__/client.test.ts`에서 malformed JSON, empty body, schema mismatch 401이 `clearSession` 0회로 3건 실패했다. GREEN 같은 command는 2 files / 21 tests passed. LSP diagnostics는 `src/shared/api/client.ts`, `src/shared/api/__tests__/client-auth.test.ts` 모두 오류 0건이었다. Auth/session focused regression `npm run test:run -- src/features/auth/tests/auth-session.test.tsx src/features/auth/tests/login-page.test.tsx src/app/App.test.tsx src/app/App.protected-auth.test.tsx`는 3 files / 24 tests passed. Broad `npm run test:run -- src/features/auth src/app`은 `REV-P10-001`/`P10-R1`의 stale fallback assertion 6건으로 실패해 기존 Phase 10 후속 범위로 유지한다.
|
||
- 2026-07-30 `P1-R7` — RED `npm run test:run -- src/shared/lib/crop-image.test.ts src/shared/ui/__tests__/image-crop-dialog.test.tsx`에서 zoom 1.5 output 600×600과 pinch 미지원으로 2 failed / 9 passed를 확인했다. 1차 review blocker 보강 RED는 210:297 zoom 1.8 output 236×334의 source height 1px 초과와 `touch-action: none` 누락으로 2 failed / 11 passed였고, tiny source 0×0 붕괴 RED는 `npm run test:run -- src/shared/lib/crop-image.test.ts` 1 failed / 6 passed였다. 2차 review blocker 보강 RED는 aspect 2 tiny source 1×0 붕괴로 `npm run test:run -- src/shared/lib/crop-image.test.ts` 1 failed / 6 passed였다. 3차 review blocker 보강 RED는 free aspect tiny source 1×2 output이 source height를 초과해 `npm run test:run -- src/shared/lib/crop-image.test.ts` 2 failed / 5 passed였다. GREEN focused는 2 files / 14 tests passed로 zoom 1.5 output 400×400, 210:297 zoom 1.8 output 236×333, tiny source 1×1, aspect 2 tiny source 1×1, free aspect tiny source 1×1, two-pointer pinch zoom 1.5와 preview `touch-action: none`을 확인했다. Broad crop regression은 23 files / 119 tests passed였다. Surface E2E `npm run e2e:mock -- tests/e2e/series.spec.ts --project=chromium`은 7 tests passed로 실제 브라우저의 Series 생성 crop Dialog open/apply 경로를 확인했다. `npm run typecheck`, `npm run lint`는 exit 0이고, `src/shared/ui`, `tests/e2e` LSP diagnostics는 오류 0건이었다. 실제 tablet touch 장비 QA는 수행하지 못해 릴리스 QA 항목으로 유지한다.
|