[실전 프로젝트] accessToken, refreshToken으로 로그인 유지하기

G-NOTE·2022년 9월 18일
0

항해99

목록 보기
34/36

들어가기 전에..

access token, refresh token이 필요한 이유 : accessToken, refreshToken

로그인을 유지하기 위해 필요한 것들

  • 서버에 request를 보낼 때 access token이 만료될 경우 그에 대한 error response를 받고 바로 토큰 갱신 request를 보내야 한다.
  • 새로운 access token을 발급받으면 바로 실패했던 request를 재요청한다.

axios.interceptors를 이용한 구현

우선 우리가 만들던 웹페이지는 토큰이 필요한 요청과 불필요한 요청이 있었다.
따라서 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);
  }
);
  • accessToken이 유효한 경우 : 정상적으로 response를 return한다.
  • accessToken이 유효하지 않은 경우
    • refreshToken이 유효한 경우
      1. error에 담긴 config(기존 요청)를 originalRequest 변수에 할당한다. : 토큰 갱신 후 재요청을 보내기 위함
      2. message === "Network Error" || response.data.errorCode === "400" : 백엔드와 사전에 논의해서 정한 에러 메시지
      3. 서버와 사전에 약속한 에러 메시지 또는 에러 코드일 경우, newAccessToken을 발급받기 위한 GET 요청을 보낸다.
      4. GET 요청이 성공했을 때 newAccessToken을 발급받는다.
      5. originalRequest headers의 Authorization을 newAccessToken으로 교체하고 axios(originalRequest)으로 재요청을 보낸다.
    • refreshToken이 유효하지 않은 경우 갖고있던 토큰(쿠키)을 지우고 홈으로 redirect시킨다.

trouble shooting

1. 토큰 갱신 후 바로 request 보내기

"유효하지 않은 요청이라 error 발생시키고, 토큰은 갱신했다. 그 다음은 어떻게 해야되지..?" 라는 생각이 들었다.
혼자 accessToken, refreshToken을 공부하고 로직을 구성하면서 고민했는데 error객체에 config라는 값이 있다는 걸 처음 알았다.
해당 요청 내용이 그대로 들어있는 config를 originalRequest라는 변수에 할당한 뒤, 토큰 갱신에 성공했을 때 originalRequest headers의 Authorization 값을 업데이트해주고 axios(originalRequest) 를 return해서 해당 request를 재전송할 수 있었다.

2. 첫 accessToken 만료 이후 토큰 갱신 뒤 계속 보내는 요청마다 accessToken이 갱신되던 문제

1번을 구현하다 내가 짚고 넘어가지 못해 생긴 문제였다.
originalRequest.headers.Authorization = response.headers.authorization;으로 기존 요청의 Authorization은 업데이트했지만 cookie에 저장되어 있던 accessToken은 바뀌지 않았고, 만료된 토큰을 계속 사용하여 요청을 보낼 때마다 accessToken이 갱신되고 있었다.

removeCookie("accessToken");
setCookie("accessToken", response.headers.authorization);

accessToken도 업데이트해주면서 문제 해결!

해결하고 보니 간단한 문제였는데 3-40분동안 백엔드랑 서버 문제인지 클라이언트 문제인지 찾느라 한참 헤맸었다..ㅋㅋ

profile
FE Developer

0개의 댓글