Skip to content

fix(reports): 제보 요청·응답 계약을 서버에 맞춤 - #17

Open
paragon0107 wants to merge 5 commits into
mainfrom
fix/report-request-contract
Open

fix(reports): 제보 요청·응답 계약을 서버에 맞춤#17
paragon0107 wants to merge 5 commits into
mainfrom
fix/report-request-contract

Conversation

@paragon0107

Copy link
Copy Markdown
Collaborator

배경

제보 저장 API 호출이 현재 상태로는 무조건 400입니다. 서버 CreateUserReportRequest가 요구하는 필수 필드 두 개가 스키마에 없습니다.

필드 서버 제약 이 브랜치 전
regionId @NotBlank @Pattern("\\d{10}") 없음
reportType @NotBlank @Pattern("PURCHASE|OBSERVED") 없음

응답 쪽도 깨져 있습니다. ResponseWrapper/api/v1/** 전체를 {code, message, data}로 감싸는데 server/reports.ts가 bare 스키마로 검증해서 파싱이 실패합니다. 라이브 API로 확인했습니다:

GET /api/v1/items?... → {"code":"SUCCESS","message":"...","data":{...}}
GET /api/v1/regions/nearby → {"code":"SUCCESS","message":"...","data":[...]}

schemas/items.tsitemPageEnvelopeSchema로 대응했지만 reports는 안 돼 있었습니다.

변경

  • regionId·reportType 추가
  • storeId를 대안으로 노출하고 store상호 배타 검증 (서버 resolveStoreId가 둘 다 오면 400)
  • createReportEnvelopeSchema 추가, createReportdata만 반환
  • 테스트 16개

서버 구현을 직접 읽고 맞춘 제약 셋

/v3/api-docs만 보면 안 드러나는 것들입니다.

1. unit은 자유 문자열이 아닙니다. CreateUserReportUseCase.validateUnititems.default_unit문자열 일치를 요구합니다. 실제 존재하는 값은 1kg·1개·1포기·100g 네 가지뿐입니다. 품목 상세의 값을 그대로 실어 보내야 하고, 사용자가 자유 입력하게 만들면 안 됩니다. 스키마 주석에 적어 뒀습니다.

2. storeIdstore는 동시에 못 보냅니다. 둘 다 없는 것은 허용됩니다(서버가 storeId=null로 저장). .refine으로 요청을 만들기 전에 막습니다.

3. regionId는 두 형태로 도착합니다. /regions/search는 문자열, /regions/nearby는 int64입니다. 서울(0 시작) 코드는 숫자로 오면서 이미 9자리로 잘립니다.

숫자에만 padStart를 적용한 게 핵심입니다. 입력 종류를 안 가리고 padStart부터 하면 8자리 시군구 코드가 "00"+8자리 = 10자리가 되어 자릿수 검증을 그대로 통과합니다. 서버는 @Pattern("\\d{10}")만 보므로 이 오답을 받아들이고 조회는 조용히 빈 결과가 됩니다.

⚠️ schemas/regions.ts의 수신측 스키마가 지금 이 상태입니다. 주석은 "여기서 터뜨리면 원인이 드러난다"고 적혀 있지만 실제로는 안 터집니다(padStart가 이미 10자리를 만들어 refine을 통과). 이 PR 범위 밖이라 손대지 않았지만 별도로 봐주시면 좋겠습니다.

검증

pnpm test 272개, pnpm build, pnpm lint 전부 통과. 최신 main(add3601) 위로 리베이스했습니다.

관련

server/reports.tscreateReport는 여전히 호출부가 없습니다 — 폼의 "확인" 버튼은 router.push(ROUTES.reportDone)만 합니다. 이 PR은 계약을 맞추는 것까지이고, 화면 연결은 별도입니다.

envelope 누락은 news·regions/nearby에도 남아 있습니다(storesae9137d에서 이미 고쳐진 걸 확인했습니다). 같은 원인이라 함께 정리할 수 있습니다.

서버 CreateUserReportRequest가 요구하는 두 필수 필드가 스키마에 없어
지금 호출하면 무조건 400이었다. 법정동 코드는 문자열/숫자 두 형태로
도착하는데, 숫자에만 padStart를 적용해 8자리 코드가 10자리로 위장하는
경로를 막는다.
ResponseWrapper가 /api/v1/** 전체를 {code, message, data}로 감싸는데
호출부가 bare 스키마로 검증해 파싱이 실패하고 있었다. items가 쓰는
envelope 패턴을 따라 감싸고 data만 반환한다.
누락 필드, reportType enum 경계, storeId·store 상호배타, 법정동 코드
자릿수 위장, envelope 파싱을 검증한다. envelope 없는 응답 거부 케이스는
직전 커밋의 회귀 방지용이다.
ponytail-review 지적 반영.

- union 두 갈래가 이미 보장하는 것을 재검사하던 .pipe 제거
- 자릿수 refine 2개를 1개로
- REPORT_TYPES·ReportType export 제거 (테스트 밖에서 아무도 import하지 않는다)
구조분해로 필드를 버리는 관용구가 no-unused-vars 경고를 냈다. omit 헬퍼로
바꿔 경고를 없애고 의도도 드러낸다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants