
이번에 코드를 리팩토링하면서 알게 된 부분이 있어 정리해두고자 한다.
그동안 나는 파일마다 import axios from 'axios'를 통해 axios를 직접 불러와 사용하는 방식으로 API 요청을 처리해왔다. 그런데 axios를 인스턴스로 생성해 공통 설정을 유틸화하면, 중복 코드를 줄이고 유지보수성도 높일 수 있다는 점을 새롭게 알게 되었다.
과거 에이전시 퍼블리셔로 일할 때는 jQuery의 ajax를 사용해본 경험이 있다. 이후에는 백엔드 개발자가 axios를 사용하는 걸 보기만 했지, 직접 써본 적은 없었다. 이번 기회에 둘의 차이점을 간단히 정리해본다.
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
axios.get("/api/data")
.then((res) => console.log(res.data))
.catch((err) => console.error(err));
결론부터 말하자면, 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를 직접 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에서 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