들어가며
API 요청 시 발생하는 에러를 각 컴포넌트에서 개별적으로 처리하다 보면 코드가 중복되고 관리가 어려워집니다. axios 인터셉터를 활용하면 이러한 에러 처리를 전역적으로 깔끔하게 관리할 수 있습니다.
axios 인터셉터 설정하기
1. 기본 인스턴스 생성
// src/api/axios.ts
import axios from 'axios';
export const instance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
},
});
2. 요청(Request) 인터셉터
instance.interceptors.request.use(
(config) => {
// 요청 보내기 전 수행할 작업
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
// 요청 에러 처리
return Promise.reject(error);
}
);
3. 응답(Response) 인터셉터
instance.interceptors.response.use(
(response) => {
// 응답 데이터 가공
return response.data;
},
(error) => {
// 에러 응답 처리
return handleAxiosError(error);
}
);
에러 처리 유틸리티 만들기
1. 에러 타입 정의
// src/types/error.ts
export interface ApiError {
status: number;
message: string;
code?: string;
}
export class CustomError extends Error {
constructor(public status: number, message: string) {
super(message);
this.name = 'CustomError';
}
}
2. 에러 핸들러 구현
// src/utils/errorHandler.ts
import { AxiosError } from 'axios';
import { ApiError } from '../types/error';
export const handleAxiosError = (error: AxiosError) => {
if (error.response) {
// 서버가 응답을 반환한 경우
const status = error.response.status;
switch (status) {
case 400:
handleBadRequest(error.response.data as ApiError);
break;
case 401:
handleUnauthorized();
break;
case 403:
handleForbidden();
break;
case 404:
handleNotFound();
break;
case 500:
handleServerError();
break;
default:
handleUnexpectedError();
}
} else if (error.request) {
// 요청은 보냈지만 응답을 받지 못한 경우
handleNetworkError();
} else {
// 요청 설정 중 에러가 발생한 경우
handleUnexpectedError();
}
return Promise.reject(error);
};
3. 상황별 에러 처리 함수
// src/utils/errorHandler.ts
const handleBadRequest = (error: ApiError) => {
toast.error(error.message || '잘못된 요청입니다.');
};
const handleUnauthorized = () => {
toast.error('로그인이 필요합니다.');
// 로그인 페이지로 리다이렉트
window.location.href = '/login';
};
const handleForbidden = () => {
toast.error('접근 권한이 없습니다.');
};
const handleNotFound = () => {
toast.error('요청한 리소스를 찾을 수 없습니다.');
};
const handleServerError = () => {
toast.error('서버 에러가 발생했습니다. 잠시 후 다시 시도해주세요.');
};
const handleNetworkError = () => {
toast.error('네트워크 연결을 확인해주세요.');
};
const handleUnexpectedError = () => {
toast.error('예기치 못한 에러가 발생했습니다.');
};
실제 사용 예시
1. API 요청 함수 작성
// src/api/user.ts
import { instance } from './axios';
export const userApi = {
getProfile: async (userId: string) => {
try {
const response = await instance.get(`/users/${userId}`);
return response;
} catch (error) {
// 전역 에러 처리기가 동작하므로 여기서는 추가 처리 불필요
throw error;
}
},
};
2. 컴포넌트에서 사용
// src/components/Profile.tsx
import { userApi } from '../api/user';
function Profile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
const fetchProfile = async () => {
try {
const data = await userApi.getProfile(userId);
setUser(data);
} catch (error) {
// 전역적으로 처리되므로 특별한 경우가 아니면 추가 처리 불필요
console.error(error);
}
};
useEffect(() => {
fetchProfile();
}, [userId]);
return (/* JSX */);
}
고급 활용
1. 재시도 로직 추가
const retryConfig = {
retries: 3,
retryDelay: (retryCount: number) => retryCount * 1000, // 1s, 2s, 3s
};
instance.interceptors.response.use(
(response) => response.data,
async (error) => {
const originalRequest = error.config;
if (error.response.status === 500 && !originalRequest._retry) {
originalRequest._retry = true;
try {
return await instance(originalRequest);
} catch (retryError) {
return Promise.reject(retryError);
}
}
return handleAxiosError(error);
}
);
2. 토큰 리프레시 로직
instance.interceptors.response.use(
(response) => response.data,
async (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const newToken = await refreshToken();
localStorage.setItem('token', newToken);
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return instance(originalRequest);
} catch (refreshError) {
// 리프레시 토큰도 만료된 경우
handleUnauthorized();
return Promise.reject(refreshError);
}
}
return handleAxiosError(error);
}
);
마무리
axios 인터셉터를 활용한 전역 에러 처리는 다음과 같은 이점이 있습니다:
- 코드 중복 감소
- 일관된 에러 처리
- 유지보수 용이성
- 사용자 경험 향상
에러 처리는 프로덕션 환경에서 매우 중요한 부분이므로, 체계적인 에러 처리 시스템을 구축하는 것이 좋습니다.
'JavaScript' 카테고리의 다른 글
[npm/yarn] package.json 스크립트 명령어 예시 (0) | 2025.03.17 |
---|---|
[JavaScript] 디바운스와 쓰로틀링 구현하고 활용하기 (0) | 2025.03.17 |
[JavaScript] sort() 숫자 정렬 안되는 문제 해결법 - 숫자 정렬 / 비교 함수 (1) | 2022.09.30 |
[JavaScript] a 태그의 href="javascript:void(0)"는 무엇인가? - intent scheme (0) | 2022.09.26 |
[JavaScript] DateTime 을 AM/PM 포맷으로 변환하는 법 / AMPM 시간 변환 (0) | 2022.09.16 |