# QA-1 Report — Backend API Contract Audit > Worker: Explore agent · Lead review: 본 세션 (overflag 조정 적용) ## Summary - Routes found: 24 (모두 `/api` prefix 적용) - Frontend calls: 24 unique - critical: 0, major: 1, minor: 3, recommendation: 1 - **verdict**: APPROVE (minor/recommendation만 남음) ## Route × Frontend call 매칭 결과 모든 24개 라우트가 frontend에서 실제 호출되며, 반대로 frontend의 모든 호출은 유효한 backend 라우트로 매핑됨. HTTP 메서드, URL, payload 키는 전부 일치. Global prefix `/api`는 `NEXT_PUBLIC_API_URL` 기본값(`https://reloop-api.nabomhalang.co.kr/api`)에 포함되어 있으므로 frontend에서는 `/auth/login` 같은 경로로만 호출해도 정상 매핑. ## Lead 리뷰 조정 사항 Worker가 올린 findings 중 일부 재분류: - **기존 [critical] `/reviews/history` Date serialization**: axios는 JSON 파싱 후 Date를 그대로 두지 않고 string으로 받는다. frontend `HistoryItem.reviewedAt: string | null`은 정확한 타입이다. `new Date(it.reviewedAt).toLocaleString()` 호출도 문제없음. → **오플래그. 제거.** - **기존 [major] ForgetCurveResponse.tag.subject 타입**: frontend 타입은 `Subject`(tags optional)지만 backend는 3필드만 select. frontend 코드가 `curve.tag.subject.tags`를 참조하지 않으므로 런타임 버그는 없다. 타입 계약이 느슨한 문제는 남지만 severity는 minor. → **major → minor로 강등.** ## Findings (정제판) ### [minor] ForgetCurveResponse 타입이 실제 응답보다 넓다 - **Location**: `frontend/src/lib/api.ts:121` - **Evidence**: `tag: { id; name; subject: Subject }` — 여기서 `Subject`는 `tags?: Tag[]`를 포함한다. 하지만 backend `stats.service.ts:27`은 `{id, name, color}`만 select한다. - **Impact**: 런타임 버그는 없지만, 향후 누군가 `curve.tag.subject.tags`를 추가하면 undefined 접근이 생긴다. - **Suggested fix**: `api.ts`의 `ForgetCurveResponse.tag.subject`를 `{ id: number; name: string; color: string }`로 좁힌다. ### [minor] `/reviews/history` 호출이 params 객체 대신 query string 리터럴 - **Location**: `frontend/src/app/review/history/page.tsx:28` - **Evidence**: `api.get('/reviews/history?limit=100')` — 다른 페이지(`study/history/page.tsx:39`)는 `{params: {limit, subjectId}}`를 쓴다. - **Suggested fix**: `api.get('/reviews/history', { params: { limit: 100 } })`. ### (철회됨) password hash 노출 추측 `me.service.ts`의 `toView()`와 `auth.service.ts`의 `safeUser()`가 whitelist로 필드를 고르므로 password는 클라이언트에 전달되지 않는다. `/auth/me`, `/auth/login`, `/auth/register`, `/me/onboarding`, `/me/profile` 응답 전부 안전하게 계산된 `onboarded: boolean` 파생값과 함께 `MeUser` shape로 정확히 돌아온다. Lead 초기 의심은 오인이었다. ### [recommendation] POST/PATCH 호출에 response type 누락 - **Location**: `study/page.tsx:72`, `subjects/page.tsx:60`, `subjects/[id]/page.tsx:57` 등 - **Evidence**: `api.post('/study-logs', ...)` — 타입 파라미터 없음. - **Impact**: 응답 shape 변경 시 런타임까지 감지 안 됨. - **Suggested fix**: 타입 파라미터 명시. ## Verdict **APPROVE** (critical/major 없음) Worker가 올린 findings를 Lead가 재검토한 결과, axios Date serialization 관련 "critical"은 오플래그였고, ForgetCurveResponse 타입 불일치는 런타임 버그로 이어지지 않는 minor. `password` hash 노출은 QA-3에서 critical로 재분류한다.