팀 프로젝트에서 Axios 세팅: createClient와 requestHandler를 이용한 설계

오병훈·2025년 4월 16일
0

✨ 들어가며

현재 스위프 9기 팀 프로젝트에서 프론트엔드 개발을 맡고 있고, 프로젝트 초기 세팅 단계에서 axios를 어떻게 구조화할지 고민하게 되었습니다.

단순히 axios.create()로 인스턴스를 하나 만들어서 쓰는 방식이 아니라, 유연하고 확장 가능한 구조가 필요하다고 느껴서 createClient, requestHandler 유틸 함수까지 설계하게 되었고, 그 과정과 이유를 정리해보려 합니다.


🤔 기본 인스턴스만으로는 부족했던 이유

처음에는 단순하게 아래와 같이 공통 인스턴스를 하나 만들고 사용했습니다.

// lib/axiosInstance.ts
import axios from 'axios';

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

export default axiosInstance;

하지만 프로젝트를 준비하는 과정에서 여러 복잡한 상황이 예상되었습니다.

  • 외부 API를 호출해야 할 수도 있음 (baseURL이 다름)

  • 요청마다 반복되는 try/catch, .data 추출 등의 로직이 중복됨


🧱 createClient: 커스텀 인스턴스를 만드는 함수

이 문제를 해결하기 위해, 인스턴스를 유동적으로 생성할 수 있도록 createClient라는 함수 형태로 구성했습니다.

// lib/axiosInstance.ts
import axios, { AxiosRequestConfig } from 'axios';

const BASE_URL = process.env.NEXT_PUBLIC_SERVER_URL;
const DEFAULT_TIMEOUT = 5000;

export const createClient = (config?: AxiosRequestConfig) => {
  return axios.create({
    baseURL: BASE_URL,
    timeout: DEFAULT_TIMEOUT,
    headers: {
      'Content-Type': 'application/json',
    },
    withCredentials: false,
    ...config,
  });
};

export const httpClient = createClient(); // 기본 인스턴스

이제 상황에 따라 다음과 같이 인스턴스를 만들 수 있습니다:

const noAuthClient = createClient({ withCredentials: false });
const externalClient = createClient({ baseURL: 'https://external.api.com' });

🔄 requestHandler: axios 요청을 추상화하는 유틸

요청 메서드마다 코드를 반복하지 않도록 requestHandler도 유틸 함수로 만들었습니다.

type RequestMethod = 'get' | 'post' | 'put' | 'delete';

export const requestHandler = async <T>(
  method: RequestMethod,
  url: string,
  payload?: T,
  config?: AxiosRequestConfig,
) => {
  const client = config ? createClient(config) : httpClient;

  try {
    let response;

    switch (method) {
      case 'post':
        response = await client.post(url, payload);
        break;
      case 'get':
        response = await client.get(url, { params: payload });
        break;
      case 'put':
        response = await client.put(url, payload);
        break;
      case 'delete':
        response = await client.delete(url, { data: payload });
        break;
      default:
        throw new Error(`지원하지 않는 요청 방식입니다: ${method}`);
    }

    return response.data;
  } catch (error) {
    console.error('API 요청 중 오류 발생:', error);
    throw error;
  }
};

📌 실제 사용 예시

// 로그인 요청
await requestHandler('post', '/auth/login', {
  email: 'test@email.com',
  password: '1234',
});

// 유저 정보 조회
await requestHandler('get', '/user/me');

// 외부 공개 API 호출
await requestHandler('get', '/public/data', undefined, {
  baseURL: 'https://external.api.com',
  withCredentials: false,
});

🙌 마무리하며

이전에는 그냥 axiosInstance.get(...)을 쓰는 게 당연하다고 생각했는데,
이번 팀 프로젝트를 통해 axios의 인스턴스를 더 유연하게 다루는 구조를 설계해보고
그 안에서 requestHandlerAPI 요청 패턴도 일관되게 추상화하면서 생산성이 훨씬 좋아졌습니다.

아직 프로젝트가 진행 중이라 이후 인터셉터, 토큰 주입, 에러 핸들링 유틸도 추가될 예정입니다.

profile
Front-End Developer

0개의 댓글