에러 처리 Feat. Axios with TanStack Query

Obebe·2026년 6월 4일

React

목록 보기
13/16
post-thumbnail

이전 프로젝트에서는 팀원분이 공통 에러 처리를 너무 깔끔하게 정리해주셨어서 편하게 사용했는데, 새로 프로젝트에 들어가며 내가 담당하게 되어 이전 내용을 복습할 겸 내용을 정리해보고자 한다.

이번에 시작하는 프로젝트에서는 API 요청 도구로 Axios를 사용하고, 서버 상태 관리를 위해 TanStack Query를 도입했다.

공통 에러 처리는 단순하게 API 요청 함수 내에서 처리하면 되는 작업이라고 생각할 수 있지만 화면이 늘어나고 API 요청이 많아졌기에 매번 직접 파싱하지 않는 방법을 찾고자했고, Axios를 사용하여 서버 에러 응답을 FE에서 사용하기 쉬운 에러 객체로 정규화하고 TanStack Query는 그 에러를 서버 상태의 일부로 관리하도록 했다.

Axios와 TanStack Query의 역할 분리

Axios와 TanStack Query는 모두 API 요청 과정에서 사용되지만, 담당하는 역할은 다르다

도구역할
AxiosHTTP 요청 수행, 공통 header 설정, 요청/응답 Interceptor 처리
TanStack Query서버 상태 캐싱, loading/error 상태 관리, retry/refetch, invalidate 처리

따라서 두 도구를 함께 사용할 때는 에러 처리 책임도 분리할 수 있다.

Axios에서는 서버 에러 응답을 프론트엔드에서 사용하기 쉬운 형태로 가공하고, TanStack Query에서는 가공된 에러 객체를 error 상태로 관리한다.

Backend Error Response
↓
Axios Response Interceptor
↓
Error Code 추출
↓
Frontend Error 객체로 정규화
↓
TanStack Query error 상태로 전달
↓
Component UI 처리

이 구조를 사용하면 컴포넌트에서 매번 서버 응답 구조를 직접 확인하지 않아도 된다.

컴포넌트는 TanStack Query가 전달한 error 객체를 기준으로 화면에 필요한 처리를 담당하면 된다.


BE 에러 응답 포맷

{
  "success": false,
  "code": "PHOTO_CARD_NOT_FOUND",
  "message": "Photo card not found"
}

이전 프로젝트와 동일하게

필드설명
success요청 성공 여부
code프론트엔드에서 에러를 식별하기 위한 값
message개발 및 디버깅을 위한 메시지

로 처리하였다.

포인트는 FE에서 message가 아닌 code를 기준으로 에러를 처리한다는 점
즉, 서버의 message는 개발자가 에러 상황을 확인하기 위한 값이고, 사용자에게 보여줄 문구는 FE에서 code를 기준으로 매핑한다.


Axios intercepter에서 에러 객체 정규화

백엔드에서 공통 응답을 내려주더라도, 각 컴포넌트에서 매번 error.response.data.code에 접근해 메시지를 매핑하면 같은 로직이 반복된다.

그래서 Axios Response Interceptor에서 서버 에러 응답을 한 번 가공하기로 했다.

Interceptor에서는 서버 응답에서 code를 추출 → 해당 코드에 맞는 사용자 메시지를 찾아 에러 객체에 추가한다.

api.interceptors.response.use(
  (response) => response,

  (error) => {
    const code = error.response?.data?.code;

    error.code = code;
    error.userMessage = getErrorMessage(code);

    return Promise.reject(error);
  }
);

이렇게 하면 이후 API 요청을 사용하는 쪽에서는 서버 응답 구조를 직접 알 필요가 없다.

try {
  await createPhotoCard(data);
} catch (error) {
  console.log(error.userMessage);
}

즉, Axios Interceptor는 서버에서 내려온 에러 응답을 프론트엔드에서 다루기 쉬운 형태로 정규화하는 역할을 한다.

# 흐름 정리
server response
{
  success: false,
  code: "INSUFFICIENT_POINT",
  message: "Not enough point"
}

↓ Axios Response Interceptor

frontend error
{
  code: "INSUFFICIENT_POINT",
  userMessage: "포인트가 부족합니다."
}

TanStack Query에서 에러 상태 다루기

TanStack Query는 queryFn 또는 mutationFn에서 발생한 에러를 error 상태로 관리한다.

이때 중요한 점은 함수 내부에서 에러가 발생하거나 rejected promise가 반환되어야 TanStack Query가 에러 상태로 인식한다는 것이다.

Axios는 HTTP 실패 응답을 자동으로 rejected promise로 처리한다. 따라서 Axios Interceptor에서 에러 객체를 가공한 뒤 Promise.reject(error)로 반환하면, TanStack Query는 이 에러를 그대로 error 상태에 담아준다.

const { data, error, isError } = useQuery({
  queryKey: ["photoCards"],
  queryFn: getPhotoCards,
});
if (isError) {
  console.log(error.userMessage);
}

Mutation에서도 같은 방식으로 에러를 다룰 수 있다.

const mutation = useMutation({
  mutationFn: createPhotoCard,
  onError: (error) => {
    console.log(error.userMessage);
  },
});

이후 각 화면에서 error.userMessage를 사용해 Toast, Modal, Form Error 등 상황에 맞는 UI 처리를 선택하면 된다.


정리

Axios
- HTTP 요청 수행
- Response Interceptor에서 Error Code 추출
- 사용자 메시지 매핑
- 에러 객체 정규화

TanStack Query
- queryFn / mutationFn 실행
- loading / error 상태 관리
- retry / refetch 관리
- 정규화된 error 객체 전달

즉 핵심은

Axios Interceptor는 에러를 가공하는 계층이고,
TanStack Query는 에러 상태를 관리하는 계층이다.

다음은 이걸 활용하여 컴포넌트 UI를 작성해보겠다.

profile
다른 건 노력의 시간

0개의 댓글