access token, refresh token이 필요한 이유 : accessToken, refreshToken
request
를 보낼 때 access token이 만료될 경우 그에 대한 error response
를 받고 바로 토큰 갱신 request
를 보내야 한다.request
를 재요청한다.우선 우리가 만들던 웹페이지는 토큰이 필요한 요청과 불필요한 요청이 있었다.
따라서 interceptors를 사용하기 위해 다른 이름을 가진 동일한 인스턴스를 두 개 생성했다.
axios.js
import axios from "axios";
import { getCookie, removeCookie, setCookie } from "api/cookies";
/* INSTANCE WITHOUT TOKEN --------------------------------------------------- */
export const instance = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
});
/* INSTANCE WITH TOKEN ------------------------------------------------------ */
export const tokenInstance = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
});
/* REQUEST INTERCEPTORS ----------------------------------------------------- */
tokenInstance.interceptors.request.use(
// 요청이 전달되기 전에 작업 수행
(config) => {
const accessToken = getCookie("accessToken");
config.headers.Authorization = `${accessToken}`;
return config;
},
(error) => {
// 요청 오류가 있는 작업 수행
return Promise.reject(error);
}
);
/* RESPONSE INTERCEPTORS ---------------------------------------------------- */
tokenInstance.interceptors.response.use(
(response) => {
// 응답 데이터가 있는 작업 수행 : STATUS CODE 2XX
return response;
},
async (error) => {
// 응답 오류가 있는 작업 수행 : STATUS CODE WITHOUT 2XX
// console.log("RESPONSE INTERCEPTORS : FAILED", error);
try {
const { message, response, config } = error;
const originalRequest = config;
if (message === "Network Error" || response.data.errorCode === "400") {
const refreshToken = getCookie("refreshToken");
/* GET : NEW ACCESSTOKEN ---------------------------------------------------- */
const response = await axios({
method: "get",
url: `${process.env.REACT_APP_BASE_URL}/auth/user/token`,
headers: {
"Content-Type": "application/json",
refreshToken: refreshToken,
},
});
/* CHANGE ACCESSTOKEN ------------------------------------------------------- */
originalRequest.headers.Authorization = response.headers.authorization;
removeCookie("accessToken");
setCookie("accessToken", response.headers.authorization);
return axios(originalRequest);
}
} catch (error) {
// 새로운 accessToken 발급에 실패한 경우 쿠키에 있던 기존 토큰을 모두 없애고 redirect
removeCookie("accessToken");
removeCookie("refreshToken");
window.location.href = "/";
return false;
}
return Promise.reject(error);
}
);
message === "Network Error" || response.data.errorCode === "400"
: 백엔드와 사전에 논의해서 정한 에러 메시지 axios(originalRequest)
으로 재요청을 보낸다."유효하지 않은 요청이라 error 발생시키고, 토큰은 갱신했다. 그 다음은 어떻게 해야되지..?" 라는 생각이 들었다.
혼자 accessToken, refreshToken을 공부하고 로직을 구성하면서 고민했는데 error객체에 config라는 값이 있다는 걸 처음 알았다.
해당 요청 내용이 그대로 들어있는 config를 originalRequest라는 변수에 할당한 뒤, 토큰 갱신에 성공했을 때 originalRequest headers의 Authorization 값을 업데이트해주고 axios(originalRequest)
를 return해서 해당 request를 재전송할 수 있었다.
1번을 구현하다 내가 짚고 넘어가지 못해 생긴 문제였다.
originalRequest.headers.Authorization = response.headers.authorization;
으로 기존 요청의 Authorization은 업데이트했지만 cookie에 저장되어 있던 accessToken은 바뀌지 않았고, 만료된 토큰을 계속 사용하여 요청을 보낼 때마다 accessToken이 갱신되고 있었다.
removeCookie("accessToken");
setCookie("accessToken", response.headers.authorization);
accessToken도 업데이트해주면서 문제 해결!
해결하고 보니 간단한 문제였는데 3-40분동안 백엔드랑 서버 문제인지 클라이언트 문제인지 찾느라 한참 헤맸었다..ㅋㅋ