이번 글에서는 CMC에서 처음 알게되었고 사용한 AxiosInstance에 대해 기술해보려고 합니다.

AxiosInstance에 대해 알아보기 전에 axios에 대해 간략하게 알아보고 넘어가봅시다.

axios는 HTTP 요청을 보낼 때 매우 유용한 JS 라이브러리인데, 이를 통해 API와의 통신을 간단하고 직관적으로 처리할 수 있습니다.
axios의 강력한 기능 중 하나는 axios.create 메서드를 사용해 기본 설정이 포함된 인스턴스를 생성할 수 있다는 점입니다.

이 인스턴스를 통해 공통 설정을 재사용하고, 요청과 응답을 전역적으로 처리하는 interceptors를 쉽게 관리할 수 있습니다.

우선 제가 프로젝트에서 사용한 코드를 보며 하나하나 살펴봅시다.



import axios from 'axios';

export const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  withCredentials: true,
});

axiosInstance 생성

먼저, axios.create 메서드를 사용해 axiosInstance를 생성합니다.

1-1. baseURL

baseURL은 모든 요청의 기본 URL이 됩니다. import.meta.env.VITE_API_BASE_URL에서 환경 변수를 통해 이 값을 동적으로 설정할 수 있는데 이를 통해 개발 환경과 프로덕션 환경에서 다른 API URL을 쉽게 설정할 수 있습니다.

1-2. withCredentials

withCredentials 옵션은 요청에 인증 정보 (예: 쿠키, 인증 header)를 포함시킬지 여부를 확인합니다.
이 옵션은 CORS 요청을 다룰 때 유용합니다.




인터셉터(Interceptors)

2-1. 요청 인터셉터(Request Interceptor)

axiosInstance.interceptors.request.use(
  config => {
    // 요청이 서버로 보내지기 전에 실행되는 로직
    // 예: Authorization 헤더 추가
    config.headers.Authorization = `Bearer ${window.localStorage.getItem('token')}`;
    return config; // config를 반환하면 다음 단계로 요청이 진행됩니다.
  },
  error => {
    // 요청이 실패했을 때 실행되는 로직
    console.error('[AXIOS_ERROR]: ', error);
    return Promise.reject(error); // 오류를 반환하여 호출자에게 전달
  }
);

요청 인터셉터는 모든 요청이 서버로 전송되기 전에 호출됩니다. 주로 인증 토큰을 추가하거나 요청을 로깅하는데 사용됩니다.

use 메서드

use 메서드는 인터셉터를 등록할 때 사용하는 메서드로 두 가지 콜백 함수를 인수로 받습니다.

첫 번째 인수

요청이 서버로 보내지기 전에 실행되는 함수입니다. 이 함수는 config 객체를 매개변수로 받으며, 이 객체를 수정하거나 그대로 반환할 수 있습니다.

두 번째 인수

요청이 실패했을 때 실행되는 함수입니다. 이 함수는 오류를 처리하거나 그대로 반환하여 호출자에게 전달할 수 있습니다.

(수정) 개념 설명만으로는 이해가 어려울 것 같아 코드에 주석을 추가했습니다.

2-2. 응답 인터셉터(Response Interceptor)

axiosInstance.interceptors.response.use(
  response => {
    // 사용자가 `/members/login` 경로로 로그인 요청을 보냈을 때, 서버로부터 받은 응답에서 인증 토큰을 `localstorage`에 저장합니다.
    if (response.config.url === '/members/login') {
      window.localStorage.setItem('token', response.data.data.token);
    }
    return response;
  },
  error => {
    if (error.response) {
      if (error.response.status === 500) {
        console.error('[AXIOS_ERROR_500]: ', error.response.data);
      }
    } else if (error.request) {
      console.error('[AXIOS_ERROR_NO_RES]:', error.request);
    } else {
      console.error('[AXIOS_ERROR_NET]: ', error.message);
    }

    return Promise.reject(error);
  }
);

응답 인터셉터는 서버로부터 응답을 받은 후 실행됩니다.

저 같은 경우는 response에서 /members/login 경로로 로그인 요청을 보냈을 때, 서버로부터 받은 응답에서 인증 tokenlocalstorage에 저장합니다. 이 token은 이후 모든 요청에 사용됩니다.

오류 처리

  • 응답에서 오류가 발생한 경우 상태 코드에 따라 적절한 처리를 합니다.
  • 예시로는 500, 500 이외의 에러가 있습니다.



axiosInstance 사용하기

이제 axiosInstance를 사용해봅시다.

// 로그인
export const signin = async (payload: SigninType) => {
  const { data } = await axiosInstance.post('/members/login', payload);
  return data;
};

필자의 경우 로그인 요청을 보낼 때 사용되는 코드를 가져왔다.

signin 함수

이 함수에서는 SigninType이라는 타입의 payload를 인수로 받습니다. 이 payload에는 로그인에 필요한 사용자 정보를 포함합니다.
비동기 함수 async를 사용했으며 내부에서는 await 키워드를 사용해 비동기 요청을 처리합니다.
사전에 설정한 axiosInstance를 사용하여 엔드포인트로 POST 요청을 보냅니다.

await 키워드

await는 비동기 요청이 완료될 때까지 함수의 실행을 일시 중지시킵니다.
요청이 성공적으로 완료되면 서버로부터의 응답이 반환되고, 응답 객체에서 data 속성만을 구조 분해 할당하여 사용합니다.

return data

서버로부터 받은 응답 데이터 data를 반환합니다.
data에는 서버가 로그인 요청에 대해 반환하는 인증 토큰이나 사용자 정보가 포함될 수 있습니다.

응답 받은 데이터 datauseSignin이라는 커스텀 훅에서 사용되는데 react-hook-form, react-query, zod 등 여러가지와 조합되어 있기 때문에 이 글에서 설명하기에는 조금 설명이 부족한 것 같아 이후 작성할 글에 이 글의 URL을 첨부하여 덧붙여 설명하려고 합니다.



장점과 활용 사례

3.1 장점

  • 코드 재사용성 증가 : 여러 컴포넌트에서 동인한 axiosInstance를 사용하여 코드 중복을 줄이고 유지 보수를 용이하게 합니다.
  • 중앙집중식 관리 : 토큰, 에러 처리 로깅 등을 중앙에서 관리할 수 있어 일관된 요청 및 응답 처리가 가능합니다.

3.2 활용 사례

  • 사용자 인증이 필요한 API 호출 : 인증이 필요한 모든 API 요청에 자동으로 토큰을 포함시킬 수 있습니다.
  • 글로벌 에러 핸들링 : 모든 API 요청의 오류를 중앙에서 처리하고, 사용자에게 알림을 띄우는 등의 일관된 에러 처리 로직을 구현할 수 있습니다.
  • 로깅 및 분석 : 모든 요청과 응답을 인터셉터에서 로깅하여 서버의 성능이나 오류 발생 현황을 추적할 수 있습니다.



결론

axios만을 사용해 본 저로서는 꽤나 신선한 경험이었던 것 같습니다. CMC에서 진행한 프로젝트가 아닌 또 다른 사이드 프로젝트에선 axios만을 사용하여 굉장히 난잡한 코드를 보여줍니다. 9월 이후 refactor예정인데 axiosInstance를 사용해 조금 더 유지보수와 간결성에 집중하려고 합니다.

레퍼런스

더 많은 자료를 원하신다면 아래 링크를 참고하시면 될 것 같습니다.

Axios Docs
https://axios-http.com/kr/docs/interceptors

Tennis_Coder