Tanstack Query는 API 호출에서 발생할 수 있는 에러도 강력하게 핸들링할 수 있는 기능을 제공함.
전역 에러 핸들링이 필요한 이유
- 중복된 에러 로직 처리
- 사용자에게 일관된 UX 제공 가능(예: toast 알림)
- 인증 실패, 서버 오류, 네트워크 오류 등의 케이스에 공통 대응 가능
// index.tsx
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => handleApiError(error),
}),
mutationCache: new MutationCache({
onError: (error, _variables, _context, mutation) => handleApiError(error),
}),
});
root.render (
<React.StrictMode>
...
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
...
</React.StrictMode>
)
defaultOptions로 에러를 제어했는데, v5로 업데이트된 이후에는 onError와 같은 리스너 함수가 기본 옵션에 직접 설정할 수 없도록 제한됨.onError, onSuccess, onSettled 등이 기본 옵션으로 허용 안함.QueryCache로 관리하는게 공식적인 방법
queryCache: 전역 쿼리 에러
fetch)에 적용useQuery, useInfiniteQuerymutationCache: 전역 뮤테이션 에러
post, put, delete등)에 적용useMutationimport axios from "axios";
import { toast } from "react-toastify";
const statusHandlers: {
[key: number]: (msg: string, status: number | null) => void;
default: (msg: string) => void;
} = {
400: (msg: string) =>
toast.error("잘못된 요청입니다. 다시 시도해 주세요.", {
toastId: "fetch-400-error",
}),
401: (msg: string, status) => {
toast.error("로그인 정보가 유효하지 않거나 세션이 만료되었습니다.", {
toastId: "fetch-401-error",
});
},
...,
500: () =>
toast.error("서버 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.", {
toastId: "fetch-500-error",
}),
default: () =>
toast.error("예기치 못한 오류가 발생했습니다. 다시 시도해 주세요.", {
toastId: "fetch-unknown-error",
}),
};
interface ErrorResponse {
message: string;
status?: number;
}
export const handleApiError = (error: unknown) => {
if (axios.isAxiosError(error)) {
if (error.response) {
const httpStatus = error.response?.status;
const errorResponse = error.response?.data?.error as ErrorResponse;
const httpMessage = errorResponse.message;
const httpErrorCode = errorResponse?.status || null;
const handle =
httpStatus !== undefined && statusHandlers[httpStatus] ? statusHandlers[httpStatus] : statusHandlers.default;
handle(httpMessage, httpErrorCode);
} else {
toast.error("서버 연결이 원활하지 않습니다.");
toast.clearWaitingQueue();
return;
}
} else {
toast.error("네트워크 연결 오류 또는 기타 오류가 발생했습니다.");
toast.clearWaitingQueue();
return;
}
};
이 유틸 함수는 이 블로그에서 도움을 받은 글임.
모든 API는 try ~ catch(error) { throw new Error(error) }문으로 에러를 잡아냈었음.
전역 쿼리로 에러를 핸들러 하려면, catch(error) { throw error } 로 해야 잡힘.