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분동안 백엔드랑 서버 문제인지 클라이언트 문제인지 찾느라 한참 헤맸었다..ㅋㅋ