Axios 인터셉터 활용
Axios 인터셉터를 활용하여 요청과 응답을 효율적으로 관리하는 방법을 살펴봅니다.
요약
- Axios 인터셉터를 사용하여 요청 전후에 공통 로직을 실행할 수 있습니다.
- 인증 토큰을 자동으로 주입하고, 응답 에러를 처리하여 사용자 경험을 개선할 수 있습니다.
- 상태 관리 라이브러리와 같이 사용할 때의 주의사항을 확인할 수 있습니다.
배경/문제
Axios는 JavaScript에서 HTTP 요청을 관리하기 위한 라이브러리로,_API_와의 상호작용을 간편하게 만들어줍니다. 특히, 인터셉터(interceptor)를 활용하면 요청 및 응답에 대해 공통 로직을 처리할 수 있어, 인증, 에러 처리, 로깅 등 다양한 기능을 구현할 수 있습니다. 하지만, 실무에서는 인터셉터 구현 시 몇 가지 문제점이 발생할 수 있습니다.
- React Hooks 사용의 제한: Axios 인터셉터 내부에서 React Hooks를 직접 사용하는 것은 불가능한데, 이는 React Hooks가 컴포넌트의 실행 흐름에서만 동작하기 때문입니다.
- 인증 에러 처리: 요청을 보내면서 인증 문제가 발생할 경우, 무한 루프에 빠지거나 반드시 예외 처리가 필요합니다.
- 복잡한 상태 관리: 상태 관리 라이브러리와 Axios 인터셉터를 통합할 때 적절한 결정을 내려야 하며, 순환 의존성에 주의해야 합니다.
접근/해결 전략
Axios 인터셉터를 통해 다양한 요구사항을 충족하기 위해서 다음과 같은 접근 방법을 사용할 수 있습니다:
- Request Interceptor: 서버로 요청이 전송되기 전에 필요한 공통 처리를 구현합니다. 여기에는 인증 토큰 주입, 요청 로깅 등이 포함됩니다.
- Response Interceptor: 서버로부터 응답이 오기 전에 에러를 처리하거나 응답 로그를 남기는 등의 처리를 일관되게 수행합니다.
이와 같이 인터셉터를 설정하는 코드는 다음과 같습니다:
import axios from 'axios';
const apiBackend = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
withCredentials: true,
});
// Request Interceptor
apiBackend.interceptors.request.use(
(config) => {
// 요청 전 처리
return config;
},
(error) => {
// 요청 에러 처리
return Promise.reject(error);
}
);
// Response Interceptor
apiBackend.interceptors.response.use(
(response) => {
// 응답 성공 처리
return response;
},
(error) => {
// 응답 에러 처리
return Promise.reject(error);
}
);
구현 포인트
Axios 인터셉터를 사용해 실질적인 구현 예제를 통해 좀 더 구체적으로 살펴보겠습니다.
인증 토큰 자동 주입
인증 토큰을 요청 헤더에 자동으로 추가하는 예제입니다:
apiBackend.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
이 코드는 로컬 스토리지에서 accessToken을 가져와 요청 헤더에 추가합니다. 이를 통해 모든 API 요청에 대해 사용자 인증을 자동으로 수행할 수 있습니다.
인증 에러 자동 처리
응답에서 인증 에러가 발생했을 때 자동으로 처리하는 방법입니다:
apiBackend.interceptors.response.use(
(response) => response,
async (error) => {
const status = error.response?.status;
const requestUrl = error.config?.url || '';
const isAuthRequest = requestUrl.includes('/auth/');
if ((status === 401 || status === 403) && !isAuthRequest) {
console.warn(`인증 에러 (${status}): ${requestUrl}`);
// Zustand 스토어에서 로그아웃 호출
const { logout, isAuthenticated } = useAuthStore.getState();
if (isAuthenticated) {
await logout();
}
}
return Promise.reject(error);
}
);
이 코드는 401 또는 403 상태 코드를 받았을 때, 인증 요청이 아닌 경우 사용자 로그아웃을 자동으로 수행합니다. 이는 무한 루프를 방지하는 데 도움이 됩니다.
요청/응답 시간 측정
요청 및 응답의 시간을 측정하여 로그를 남기는 방법도 유용합니다:
apiBackend.interceptors.request.use((config) => {
config.metadata = { startTime: Date.now() };
return config;
});
apiBackend.interceptors.response.use(
(response) => {
const duration = Date.now() - response.config.metadata.startTime;
console.log(`[API] ${response.config.url} took ${duration}ms`);
return response;
},
(error) => {
if (error.config?.metadata) {
const duration = Date.now() - error.config.metadata.startTime;
console.log(
`[API Error] ${error.config.url} failed after ${duration}ms`
);
}
return Promise.reject(error);
}
);
주의사항/트레이드오프
Axios 인터셉터를 사용할 때 고려해야 할 주의사항과 트레이드오프는 다음과 같습니다.
순환 의존성 방지
인터셉터에서 상태 관리 스토어를 사용할 때 순환 의존성을 피하는 것이 중요합니다. 다음과 같은 구조는 문제를 일으킬 수 있습니다:
// apiBackend.ts에서 authStore import 시 주의
// authStore.ts도 apiBackend.ts를 import하면 순환 의존성 발생
이를 방지하기 위해 동적 import 또는 lazy 패턴을 사용하는 것이 좋습니다:
const getAuthStore = () =>
import('@/stores/authStore').then((m) => m.useAuthStore);
무한 루프 방지
인증 요청을 하는 과정에서 로그아웃 API가 401을 반환할 경우 무한 루프가 발생할 수 있습니다. 따라서 아래와 같이 처리해야 합니다:
apiBackend.interceptors.response.use(
(response) => response,
async (error) => {
const isLogoutRequest = error.config?.url?.includes('/logout');
if (error.response?.status === 401 && !isLogoutRequest) {
await logout();
}
return Promise.reject(error);
}
);
마무리
Axios 인터셉터를 통해 요청 및 응답을 효율적으로 처리할 수 있는 여러 가지 해법을 살펴보았습니다. 다음과 같은 체크리스트를 통해 전체적인 구조와 로직을 점검해볼 수 있습니다:
- 인증 토큰 주입 로직이 올바르게 작동하는가?
- 오류 응답을 적절하게 처리하고 있는가?
- 상태 관리 스토어와의 연동에서 순환 의존성 문제가 없는가?
- 타임아웃 및 응답 속도를 로깅하여 모니터링하고 있는가?
이러한 요소들을 고려하여 Axios 인터셉터를 효과적으로 활용하시길 바랍니다.