본문 바로가기

카테고리 없음

[Web] accessToken & refreshToken 처리를 위한 axios interceptors 설정

필요한 설정 내용

1. 요청시 로컬스토리지에 저장되어있는 access Token을 헤더에 담아서 보내기
2. 로컬 스토리지에 access Token이 없으면 컬 스토리지를 비우고 로그인 페이지로 보내기
3. access Token이 만료되어 401 응답을 받는 경우 access Token을 재발행 하기
4. access Token을 새로 발급 받고 있는 중에 들어온 작업들은 저장해 뒀다가 순차 실행하기
5. refresh Token도 만료되면 로컬 스토리지를 비우고 로그인 페이지로 보내기

 

import axios from "axios";


const axiosInstance = axios.create({
  // timeout: 1000, // 세션만료 시간
  headers: {
    "Content-Type": "application/json",
  },
});
axiosInstance.interceptors.request.use(
  (config) => {
      /* config에는 위의 axiosInstance 객체를 이용하여 request를 보냈을떄의 모든
    설정값들이 들어있다.
    * 활용 *
    1. api요청의 경우 token이 필요한 경우가 있는데, 필요에 따른 토큰 정보들을 여기서 처리할 경우
    토큰에 대한 정보를 여러곳에서 처리하지 않아도 된다.
    2. 요청 method에 따른 외부로 드러내지 않고 처리하고 싶은 부분에 대한 작업이 가능.
    **/
   const accessToken = localStorage.getItem('accessToken');
   if(accessToken){
      config.headers.Authorization = `Bearer ${accessToken}`;
   }else{
    localStorage.removeItem("accessToken");
    localStorage.removeItem("refreshToken");
    window.location.href = '/login'
   }
    return config;
  },
  (err) => {
      /**
    request 요청을 보낼때 error가 발생하는 경우 여기서 catch가 가능하다.
    */
    return Promise.reject(err);
  }
);

let isRefreshing = false; // 발행중 flag
let failedQueue = []; // 할 일 리스트

// 저장해둔 할 일 수행
const processQueue = (error, token=null) => {
  failedQueue.forEach(prom => {
    if(error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  })
  failedQueue = [];
}

axiosInstance.interceptors.response.use(
  (config) => {
      /* 요청을 보낸 뒤에 response(응답)이 오는 경우에 여기서 먼저 확인이 가능하다.
    * 활용 *
    1. status-code가 정상적이어도 내용상의 이유로 에러처리가 필요한 경우
    2. 민감정보 또는 데이터에 대한 가공 작업
    **/
    return config;
  },
  (err) => {
    const originalRequest = err.config;

    if(err.response.status === 401 && !originalRequest._retry){
      
      if(isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({resolve, reject})
        }).then(token => {
          originalRequest.headers['Authorization'] = `Bearer ` + token;
          return axios(originalRequest);
        }).catch(err => {
          return err
        })
      }

      originalRequest._retry = true;
      isRefreshing = true;

      const accessToken = localStorage.getItem('accessToken');
      const refreshToken = localStorage.getItem('refreshToken');

      const tokenData = {
        accessToken,
        refreshToken,
      };

      return new Promise(function(resolve, reject) {
        axios.post('/member/reissue', tokenData)
        .then(({data}) => {
          localStorage.setItem('accessToken', data.tokenInfo.accessToken);
          originalRequest.headers.Authorization = `Bearer ${data.tokenInfo.accessToken}`;
          resolve(axios(originalRequest))
          processQueue(null, data.tokenInfo.accessToken);
        })
        .catch((err) => {
          processQueue(err, null);
          reject(err);
          localStorage.removeItem("accessToken");
          localStorage.removeItem("refreshToken");
          window.location.href = '/login'
        })
        .then(() => { isRefreshing = false })
      })
    }
    return Promise.reject(err);
  }
);
export default axiosInstance;

 

 

github 주소 : https://github.com/eugenekk/axiosConfig.js/blob/main/axiosConfig.js

 

GitHub - eugenekk/axiosConfig.js: accessToken & refreshToken 처리를 위한 axios Interceptor 설정한 axiosConfig 파일

accessToken & refreshToken 처리를 위한 axios Interceptor 설정한 axiosConfig 파일 - GitHub - eugenekk/axiosConfig.js: accessToken & refreshToken 처리를 위한 axios Interceptor 설정한 axiosConfig 파일

github.com