interceptors 단어의 뜻 : 가로채는 사람, 방해자, 장애물
axios interceptors는 중간에서 request나 response를 가로채가는 기능이다.
then
과 catch
로 넘어가기 전)에 인터셉터로 가로채서 원하는 작업들을 추가할 수 있다. // 요청 인터셉터
axios.interceptors.request.use(function (config) {
// 1. 요청 전달되기 전 작업 처리
// config를 설정할 수 있다
return config;
}, function (error) {
// 2. 요청 에러가 있는 작업 처리
return Promise.reject(error);
});
// 응답 인터셉터
axios.interceptors.response.use(function (response) {
// 응답 200번대 status일 때 응답 성공 직전 호출
// 3. 이 작업 이후 .then()으로 이어진다
return response;
}, function (error) {
// 응답 200번대가 아닌 status일 때 응답 에러 직전 호출
// 4. 이 작업 이후 .catch()로 이어진다
return Promise.reject(error);
});
JWT Access 토큰을 사용할 때 매 API요청마다 로그인 여부를 확인해서 헤더에 Authorization을 넣지 않아도
요청 전 interceptors에서 설정해서 헤더에 토큰을 미리 넣을 수 있다.
- 위 예시는 JWT를 로컬스토리지에 넣어서 저장한 경우에 해당된다.
하지만 로컬스토리지는 XSS 공격에 취약한 보안 문제가 있기 때문에 토큰의 저장소로는 비권장되기 때문에 로컬스토리지로 구현한 경우에만 참고하길 바란다.
Access 토큰이 만료됐는지 확인하여 Refresh 토큰을 헤더에 담는 세팅을 미리 할 수 있다. 만료가 됐는데도 Refresh 토큰없이 Access 토큰만 헤더에 담아서 요청을 보낸다면 로그인이 안되기 때문에, Refresh토큰으로 Access토큰을 재발급해오는 로직을 짤 수 있다.
공통된 error들에 대한 처리가 있다면 응답이 then으로 넘어가기 전에 error 처리를 사전에 할 수 있다.
JWT 인증방식 로그인 + 토큰 로컬스토리지에 저장
- 로컬스토리지는 XSS공격에 취약하므로 이 방법이 정답이 아니다!
특정 프로젝트에서 밑과 같이 구현했다.
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { ACCESS_EXP_MESSAGE, CheckJWTExp } from 'utils/CheckJwtExp';
import {
getLocalStorage,
removeLocalStorage,
setLocalStorage,
} from './localStorage';
axios.defaults.withCredentials = true;
/** 1. 요청 전 - access토큰있는데 만료되면 refresh토큰도 헤더담아서 요청보내기 */
axios.interceptors.request.use(
(config: AxiosRequestConfig) => {
const accessToken = getLocalStorage('access_token');
const refreshToken = getLocalStorage('refresh_token');
if (accessToken) {
/** 2. access토큰 있으면 만료됐는지 체크 */
if (CheckJWTExp() === ACCESS_EXP_MESSAGE) {
/** 3. 만료되면 만료된 access, refresh 같이 헤더 담아서 요청 */
config.headers!.Authorization = `${accessToken}`;
config.headers!.Refresh = `${refreshToken}`;
} else {
config.headers!.Authorization = `${accessToken}`;
}
}
return config;
},
(error) => Promise.reject(error)
);
/** 4. 응답 전 - 새 access토큰받으면 갈아끼기 */
axios.interceptors.response.use(
async (response: AxiosResponse) => {
if (response.headers.authorization) {
const newAccessToken = response?.headers?.authorization;
removeLocalStorage('access_token'); // 만료된 access토큰 삭제
setLocalStorage('access_token', newAccessToken); // 새걸로 교체
response.config.headers = {
authorization: `${newAccessToken}`,
};
}
return response;
},
(error) => {
//응답 200 아닌 경우 - 디버깅
return Promise.reject(error);
}
);