프로젝트 초기에 Axios interceptor 설정하기
서론
지난 주에는 사이드 프로젝트에서 사용할 라이브러리를 선택하고 공부하는 시간을 가졌다. 이제 본격적으로 프로젝트를 세팅하고 있는 중인데, Axios를 세팅하는 김에 블로그에 정리하며 깊이 있게 공부하면 좋을 것 같아 이렇게 글을 작성한다.
Axios란?
Axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트이다. HTTP 요청(GET, POST, PUT, DELETE 등)을 손쉽게 처리할 수 있도록 도와주며, 주로 RESTful API와 통신하기 위해 사용된다.
쉽게 말하자면, 백엔드와 프론트엔드 간의 통신을 쉽게하기 위해 존재하는 라이브러리라고 할 수 있다.
설치
$ npm install axios // npm
$ yarn add axios // yarn
Axios의 주요 특징
- Promise 기반
- 비동기 처리에 async/await 사용 가능.
- 자동 JSON 변환
- 요청 데이터와 응답 데이터를 자동으로 JSON으로 변환.
- 요청 취소
- 요청을 취소할 수 있는 CancelToken 제공.
- interceptor 제공
- 요청과 응답을 가로채고 처리할 수 있는 기능 제공.
- 브라우저와 Node.js에서 모두 사용 가능
- 클라이언트와 서버 모두에서 동일한 방식으로 사용 가능.
오늘은 위 주요 특징 중 장점이라고 할 수 있는 interceptor에 대해서 알아보도록 할 것이다.
Axios interceptor를 사용하기 전의 코드
Axios interceptor를 사용하기 전에는 위와 같이 매번 요청마다 header를 넣어줘야 한다는 번거로움이 존재한다.
뿐만 아니라 아래의 코드에서도 알 수 있듯이, 응답 요청을 처리하는 로직(response.status)도 중복해서 존재한다.
이러한 요소들은 코드 가독성의 저하와 더불어서 유지보수 또한 어렵게 만든다는 단점이 있다.
import axios from 'axios';
async function getUserData() {
try {
const token = localStorage.getItem('token');
const response = await axios.get('https://api.example.com/user', {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(response.data);
} catch (error) {
if (error.response && error.response.status === 401) {
console.error('Unauthorized! Redirecting to login...');
window.location.href = '/login';
} else {
console.error('An error occurred:', error.message);
}
}
}
Axios interceptor 사용한 후의 코드
아래의 코드를 보면 알 수 있듯이, Axios interceptor를 사용한 후의 코드는 놀랍도록 간결한 것을 확인할 수 있다.
import api from "@/app/api/requestApi";
async function getUserData() {
try {
const userData = await api.get('/user');
console.log(userData);
} catch (error) {
console.error('Error fetching user data:', error.message);
}
}
이것이 가능한 이유는 axios interceptor가 애플리케이션에서 처리하기 전에 Axios 라이브러리에서 수행한 HTTP 요청 또는 응답을 가로채고 수정하는 데 사용할 수 있기 때문이다. 우리는 요청을 가로채는 역할을 하는 interceptor를 만들고 싶으면 우선 api에 해당하는 axios instance를 만들어야한다.
(1) axios instance 생성
const apis = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
- baseURL
- 엔드포인트의 기본 URL로, axios 인스턴스에서 보내는 모든 요청의 기본 URL을 지정
- 요청을 보낼 때 상대적인 URL만 사용하면 해당 baseURL이 자동으로 앞에 붙게 됨.
- timeout
- 요청에 대한 타임아웃을 설정
- 이 시간(밀리초) 이내에 서버로부터 응답을 받지 못하면 요청이 실패하고 에러발생
- headers
- HTTP 헤더를 나타내는 객체로, 요청 헤더에 추가할 사용자 지정 헤더를 지정하는 데 사용
(2) interceptor requeset 작성
interceptor requeset는 HTTP 요청이 서버로 전송되기 직전에 실행 되며, 요청 데이터를 가공하거나 추가 작업을 수행할 때 사용된다.
사용하는 곳
- 헤더 추가: 인증 토큰(Authorization) 삽입.
- 요청 데이터 변환: JSON 변환, 추가 데이터 첨부.
- 로깅: 요청 정보를 로깅.
import axios from 'axios';
const apis = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
// Request Interceptor 설정
apis.interceptors.request.use(
(config) => {
// 요청 전에 헤더에 인증 토큰 추가
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log('Request:', config);
return config; // 반드시 config를 반환해야 함
},
(error) => {
// 요청 에러 처리
console.error('Request Error:', error);
return Promise.reject(error);
}
);
// 요청 호출
apis.get('/user'); // 헤더에 자동으로 Authorization이 추가됨
이제, 위 코드에서는 API 요청이 생길 때마다 localStorage에 존재하는 token을 가져와 Authorization header에 넣어준다.
(3) interceptor response 작성
서버로부터 응답을 받은 후, .then() 또는 .catch()로 처리되기 직전에 실행 되며, 서버의 응답 데이터를 가공하거나 에러를 중앙에서 처리할 때 사용된다.
사용하는 곳
- 응답 데이터 전처리: 서버 응답에서 필요한 데이터만 추출.
- 에러 공통 처리: 인증 만료(401)나 권한 없음(403) 등의 에러 핸들링.
- 재시도 로직: 특정 상태 코드일 경우 요청을 다시 시도.
apis.interceptors.response.use(
(response) => {
// 응답 데이터 가공
console.log('Response:', response);
return response.data; // 필요한 데이터만 반환
},
(error) => {
// 에러 처리
if (error.response && error.response.status === 401) {
console.error('Unauthorized! Redirecting to login...');
window.location.href = '/login'; // 로그인 페이지로 리다이렉션
} else if (error.response && error.response.status === 500) {
console.error('Server Error! Please try again later.');
}
return Promise.reject(error); // 에러를 다음으로 전달
}
);
// 요청 호출
apis.get('/user')
.then((data) => console.log(data))
.catch((error) => console.error(error));
위 코드에서는 서버 응답 데이터를 가공하거나 에러를 공통적으로 처리하는 로직이 담겨져 있다.
종합 코드
import axios from 'axios';
const apis = axios.create({
baseURL: 'https://api.example.com',
});
// Request Interceptor
apis.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log('Request Sent:', config);
return config;
},
(error) => Promise.reject(error)
);
// Response Interceptor
apis.interceptors.response.use(
(response) => {
console.log('Response Received:', response);
return response.data;
},
(error) => {
if (error.response?.status === 401) {
console.error('Unauthorized! Redirecting to login...');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// API 호출
apis.get('/user')
.then((data) => console.log('User Data:', data))
.catch((error) => console.error('API Error:', error));