axiosInstance 만들어서 사용해보기

해니니·2025년 3월 30일
post-thumbnail

이번에 코드를 리팩토링하면서 알게 된 부분이 있어 정리해두고자 한다.

그동안 나는 파일마다 import axios from 'axios'를 통해 axios를 직접 불러와 사용하는 방식으로 API 요청을 처리해왔다. 그런데 axios를 인스턴스로 생성해 공통 설정을 유틸화하면, 중복 코드를 줄이고 유지보수성도 높일 수 있다는 점을 새롭게 알게 되었다.


axios란 뭘까 ajax와는 다른점

과거 에이전시 퍼블리셔로 일할 때는 jQuery의 ajax를 사용해본 경험이 있다. 이후에는 백엔드 개발자가 axios를 사용하는 걸 보기만 했지, 직접 써본 적은 없었다. 이번 기회에 둘의 차이점을 간단히 정리해본다.

Ajax(Asynchronous JavaScript and XML)

  • 자바스크립트를 이용해 브라우저와 서버 간 데이터를 비동기적으로 주고받는 기술
  • XMLHttpRequest 객체를 활용
  • 페이지를 새로고침하지 않고도 데이터 갱신 가능
  • 다소 번거롭고 가독성이 낮은 코드가 자주 발생
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.onload = function () {
  if (xhr.status === 200) {
    console.log(xhr.responseText);
  }
};
xhr.send();

Axios

  • Promise 기반의 HTTP 클라이언트 라이브러리
  • 브라우저 및 Node.js 환경 모두 지원
  • 직관적인 문법, 자동 JSON 변환, 요청/응답 인터셉터, 에러 핸들링 등 다양한 기능 제공
  • 현대 웹 개발에서 표준처럼 사용됨
axios.get("/api/data")
  .then((res) => console.log(res.data))
  .catch((err) => console.error(err));

axiosInstance 가 필요한 이유

결론부터 말하자면, axiosInstance는 코드의 재사용성, 유지보수성, 효율성을 높이기 위한 도구다.

공통 설정의 중앙화

API 요청 시 반복적으로 사용되는 설정(예: baseURL, headers, 인증 토큰 등)을 한 곳에 모아 관리할 수 있다.

import axios from "axios";

const api = axios.create({
  baseURL: "https://api.example.com",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${localStorage.getItem("token")}`,
  },
});

export default api;

코드 재사용성 향상

인스턴스를 사용하면 동일한 설정을 매번 반복하지 않아도 되며, API 요청 로직을 모듈화해 여러 컴포넌트에서 쉽게 재사용할 수 있다.

// api/userApi.ts
import api from "./axiosInstance";

export const fetchUser = () => api.get("/user");


// 컴포넌트 내
const { data } = useQuery(["user"], fetchUser);

인터셉터를 통한 요청/응답 처리

axiosInstance는 인터셉터를 통해 모든 요청과 응답에 대한 전처리 및 후처리를 쉽게 설정할 수 있다.

api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response.status === 401) {
      // 예: 토큰 갱신 또는 로그아웃 처리
    }
    return Promise.reject(error);
  }
);

환경별 설정 관리

테스트, 개발, 프로덕션 등 환경에 따라 baseURL이 달라질 경우 .env 파일과 연동하여 자동으로 적용할 수 있다.

const api = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
});

유지보수성과 확장성

공통 설정이 한 곳에 집중되어 있기 때문에, 인증 방식 변경, 헤더 추가 등 전역 수정이 필요할 때 단 한 곳만 수정하면 된다. 프로젝트 규모가 커질수록 인스턴스 패턴은 필수에 가깝다.


실제 적용 예시

axios 인스턴스를 사용하면, 단순한 요청뿐만 아니라 에러 처리 구조도 일관되게 통일할 수 있다. 아래는 게시글 상세 정보를 조회하는 API 요청 코드다.

리팩토링 전: axios 직접 사용

예전에는 다음과 같이 axios를 직접 import해서 요청마다 URL, 헤더, 에러 처리 로직을 반복 작성했었다.

import axios from 'axios';

export async function fetchPost(id: string): Promise<Post> {
  try {
    const response = await axios.get(`${process.env.NEXT_PUBLIC_LOCAL_BACKEND_URL}/posts/${id}`, {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${Cookies.get('accessToken')}`,
      },
    });
    return response.data;
  } catch (error) {
    console.error('Failed to fetch post:', error);
    throw new Error('게시글을 불러오는 데 실패했습니다.');
  }
}

요청마다 baseURL, 헤더, 토큰을 반복 설정해야 했고 재사용 불가능하고 유지보수가 어려웠다.

리팩토링 후, axiosInstance + 상수화된 에러 메시지

axiosInstance에서 baseURL, 토큰 등 공통 설정을 자동 적용했다.
에러 메세지도 같이 상수화했고 요청 코드가 간결해지고 유지보수가 훨씬 더 쉬워진 것을 확인할 수 있다.

// /services/common/axiosInstance.ts
import axios from 'axios';
import Cookies from 'js-cookie';

export const axiosInstance = axios.create({
  baseURL: process.env.NEXT_PUBLIC_LOCAL_BACKEND_URL,
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// 요청 인터셉터
axiosInstance.interceptors.request.use((config) => {
  const accessToken = Cookies.get('accessToken');

  // 인증이 필요한 요청에만 토큰 추가
  if (accessToken && config.headers?.requiresAuth) {
    config.headers.Authorization = `Bearer ${accessToken}`;
  }

  return config;
});
import { axiosInstance } from '@/services/common/axiosInstance';
import { API_URLS } from '@/constants/urls';
import { ERROR_MESSAGES } from '@/constants/errors';
import { AxiosError } from 'axios';

export async function fetchPost(id: string): Promise<Post> {
  try {
    const response = await axiosInstance.get(`${API_URLS.POSTS}/${id}`);
    return response.data;
  } catch (error) {
    if (error instanceof AxiosError) {
      let errorMessage: string = ERROR_MESSAGES.CLIENT_ERROR;

      if (error.response?.status === 404) {
        errorMessage = ERROR_MESSAGES.POST_NOT_FOUND;
      } else if (error.response?.status === 500) {
        errorMessage = ERROR_MESSAGES.SERVER_ERROR;
      }

      console.error(errorMessage);
      throw new Error(errorMessage);
    } else {
      console.error('Failed to fetch post:', error);
      throw new Error(ERROR_MESSAGES.UNKNOWN_ERROR);
    }
  }
}

결론

axiosInstance는 프론트엔드 개발에서 API 호출 로직을 간소화하고, 코드의 재사용성을 높이며, 유지보수를 용이하게 만드는 데 필수적인 도구다. 이를 통해 개발자는 비즈니스 로직에 더 집중할 수 있고, 코드 품질 또한 개선된다.

돌이켜보면 이제는 너무나 당연하게 느껴지는 개념이지만, 이전에는 왜 이걸 모르고 매번 따로 불러 썼을까 싶다. 이번 리팩토링을 통해, 작은 습관 하나가 전체 프로젝트에 긍정적인 영향을 줄 수 있다는 사실을 다시 한번 느꼈다. 공부하자!


참고한 사이트들
https://apidog.com/kr/blog/axios-response-2/
https://velog.io/@sgsg9447/%EC%8A%A4%ED%84%B0%EB%94%94-%EA%B4%80%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-api-axios-instance
https://sangcho.tistory.com/entry/Axios-digging

profile
Front.Dev 연습생

0개의 댓글