Vue 화면에 목록이 하나도 안 나오는데 어떤 오류 메시지도 함께 뜨지 않았다. DevTools 의 Network 탭으로 확인해 보니 요청은 200 이었고 본문 내용도 들어 있었다.
Table of contents
Open Table of contents
원인 — 200인데 빈 화면
요청 자체는 성공했고 응답 본문을 파싱하는 것도 아무 문제 없이 끝났다. 실제로 실패한 자리는 그 응답을 받아서 다루는 그다음 처리 쪽이었다.
try 구간 밖에서 일어나거나 v-for 렌더링 쪽이 삼키면 오류 표시 없이 빈 화면만 남는다. 요청 성공 여부만 보는 오류 처리는 응답 이후의 실패를 통째로 못 잡는다.
검증 — 봉투 객체와 배열 타입
응답 본문을 열어 보니 { data: [...], errors: [...] } 봉투 객체였다. 그런데 화면 쪽 코드는 그것을 Product[] 로 타입해 두고 받자마자 .map() 으로 순회하고 있었다.
객체를 배열처럼 다루면 그 자리에서 실패하는데 그 실패가 렌더링 안에서 일어나 조용히 빈 화면이 됐다. 응답이 배열로 온다는 전제가 코드 어디에서도 검증되지 않고 있었다.
조치 — 타입 선언과 런타임 값
타입이 선언돼 있으니 맞을 것으로 봤지만 타입 선언은 작성 시점의 약속이고 런타임에 오는 값과 다를 수 있다. 서버가 응답 모양을 바꾸더라도 화면 쪽의 타입 선언은 그대로 남아 있게 된다.
두 가지 방법이 있었는데 이 라우트 하나만 고치는 것과 라우트 단위로 실제 응답을 받아 types/api.ts 의 선언과 전부 대조하는 것이다. 뒤쪽을 골랐다. 이유는 서버가 봉투 형태로 바꾼 시점이 있다면 다른 라우트도 같이 바뀌었을 가능성이 커서다. 대신 라우트 수만큼 curl 을 돌리는 반나절을 감수했다. 대조했더니 안 맞는 곳이 여럿이었다.
주의 — 문자열로 오는 소수
대조하는 과정에서 price 소수 필드가 숫자가 아니라 "12.50" 문자열로 온다는 것도 확인했다. PHP 의 PDO 가 DECIMAL 컬럼을 정밀도를 지키려고 문자열로 넘기고 json_encode 가 그대로 내보내고 있었다.
그 값을 그대로 곱하면 계산이 안 되고 더하면 문자열이 그대로 이어 붙는다. 뒤쪽이 특히 나쁜데 오류가 나지 않은 채로 그럴듯한 값이 만들어지기 때문이다.
배경 — 권한과 낡은 주석
편집 화면을 만들었는데 저장이 항상 403 으로 거부되어 확인하니 PUT 경로는 다른 종류의 계정만 쓸 수 있었다. 같은 리소스여도 조회와 수정의 요구 권한이 다를 수 있으므로 화면을 만들기 전에 확인할 항목이었다.
파일 상단에는 백엔드가 미구현이라 mock.json 을 쓴다는 주석이 있었는데 실제로는 이미 구현돼 있었다. 주석보다 실제 경로를 확인해야 했고 이번 정리는 보고받은 내용을 옮긴 것이라 직접 재검증하지 못했다는 근거 수준도 함께 적었다.
정리
- 응답 모양이 달라도 요청은 성공으로 끝난다
- 요청 성공만 보는 오류 처리는 그 뒤의 실패를 못 잡는다
- 배열인 줄 알았던 응답이 봉투 객체일 수 있다
- 타입 선언은 검증이 아니라 작성 시점의 약속이다
- 라우트 단위로 실제 응답과 대조한다
- 소수 필드가 문자열로 오면 오류 없이 값만 틀린다
- 같은 리소스여도 조회와 수정의 요구 권한이 다를 수 있다
- 주석은 낡으므로 실제 경로를 확인하고 근거 수준을 적는다