Epic: API Status Code 전수 대응 #298
우선순위: P0 (최우선 · 선행)
배경
에러 무 처리의 근본 원인은 개별 훅이 아니라 전역 설정이다.
utils/queryClient.ts에 queries defaultOptions만 있고 mutations 전역 onError가 없다. MutationCache도 미사용
- → onError가 비어있거나(
// TODO) 없는 mutation은 실패해도 조용히 삼킨다(silent failure). 사용자는 성공한 줄 안다
즉 위시/토너먼트/알림 이슈의 "무처리" 상당수는 개별 수정 이전에 전역 안전망 하나로 대부분 해결된다.
설계 원칙
📖 전체 규약 파일 생성: error-handling-policy.md
- 4xx = 개별 처리 / 5xx·네트워크 = 전역 처리 (핵심 모델)
- 5xx는 전역이 단독 권위 — 개별 onError는 5xx를 건드리지 않음 (토스트 중복 방지)
- 4xx는 개별 우선, 개별 onError 없으면 전역 generic fallback
- 명시적 처리 = 도달 가능하고 사용자 행동이 바뀌는 것만 (400 검증, 409 만료 등)
- "정상 UI에선 도달 불가"한 케이스(예: 남의 위시 403)는 개별 분기 X → 안전망이 덮음
대응 방안
// 예시 (utils/queryClient.ts) — 5xx 전역 독점 / 4xx 개별 우선
new QueryClient({
mutationCache: new MutationCache({
onError: (error, _vars, _ctx, mutation) => {
const status = isAxiosError(error) ? error.response?.status : undefined;
const isServerError = !status || status >= 500; // 5xx + 네트워크
if (isServerError) {
toast.error(getApiErrorMessage(error));
// Sentry.captureException(error);
return; // 5xx는 전역 독점 → 개별은 5xx 토스트 금지
}
if (mutation.options.onError) return; // 4xx: 개별 처리 우선
toast.error(getApiErrorMessage(error));
},
}),
defaultOptions: { queries: { retry: 1, staleTime: 60_000, refetchOnWindowFocus: false } },
});
⚠️ MutationCache.onError와 개별 onError는 둘 다 실행됨 → 개별 훅은 반드시 4xx만 분기 (5xx 브랜치 삭제).
⚠️ 개별 onError 정리 규칙 (중요)
전역 4xx fallback은 개별 onError가 "없을 때만" 작동한다 (if (mutation.options.onError) return). 따라서:
- 비어있는 onError(
// TODO만) → "존재"로 취급돼 전역이 양보 → 여전히 silent. → 삭제할 것
- 부분 onError(예: 403/404만, 400 빠짐) → 빠진 4xx는 개별도 전역도 안 잡음 → 여전히 silent. → 도달 가능한 4xx 전부 커버할 것
규칙: onError는 "4xx 전부 책임" 또는 "아예 없음(전역 위임)" 둘 중 하나. 비어있는/부분적인 onError 금지.
완료 조건 (AC)
- onError 없는 mutation이 실패해도 최소 generic 토스트가 반드시 뜬다 (silent failure 0)
- 개별 맞춤 onError는 그대로 우선 동작 (이중 토스트 없음)
- 예상 못한 에러가 Sentry에 기록됨
Epic: API Status Code 전수 대응 #298
우선순위: P0 (최우선 · 선행)
배경
에러 무 처리의 근본 원인은 개별 훅이 아니라 전역 설정이다.
utils/queryClient.ts에 queriesdefaultOptions만 있고 mutations 전역 onError가 없다.MutationCache도 미사용// TODO) 없는 mutation은 실패해도 조용히 삼킨다(silent failure). 사용자는 성공한 줄 안다즉 위시/토너먼트/알림 이슈의 "무처리" 상당수는 개별 수정 이전에 전역 안전망 하나로 대부분 해결된다.
설계 원칙
대응 방안
QueryClient에MutationCache전역onError추가 (utils/queryClient.ts)onError가 있으면 스킵 (맞춤 처리 우선)getApiErrorMessage(error)— 서버detail있으면 노출, 없으면 기본 문구)QueryCache전역onError추가 — Sentry 로깅만 (토스트 X) (query는 배경 refetch 스팸 방지)getApiErrorMessage(error)정의 (5xx/네트워크/detail처리 일원화 — #3와 공유)전역 4xx fallback은 개별 onError가 "없을 때만" 작동한다 (
if (mutation.options.onError) return). 따라서:// TODO만) → "존재"로 취급돼 전역이 양보 → 여전히 silent. → 삭제할 것완료 조건 (AC)