에러 핸들링 with Tanstack Query #1

꾸준히·2025년 6월 24일

Tanstack Query

목록 보기
4/5

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>
)

V5 이전

  • 이전에는 defaultOptions로 에러를 제어했는데, v5로 업데이트된 이후에는 onError와 같은 리스너 함수가 기본 옵션에 직접 설정할 수 없도록 제한됨.
  • 즉, onError, onSuccess, onSettled 등이 기본 옵션으로 허용 안함.

v5 이후

  • QueryCache로 관리하는게 공식적인 방법

  • queryCache: 전역 쿼리 에러

    • 읽기 쿼리(fetch)에 적용
    • useQuery, useInfiniteQuery
  • mutationCache: 전역 뮤테이션 에러

    • 쓰기 작업(post, put, delete등)에 적용
    • useMutation

핸들러 유틸 함수

import 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 status는 현재 내가 사용하고 있는 API 응답값을 기반으로 만들었음.

적용하기

모든 API는 try ~ catch(error) { throw new Error(error) }문으로 에러를 잡아냈었음.

전역 쿼리로 에러를 핸들러 하려면, catch(error) { throw error } 로 해야 잡힘.

0개의 댓글