[로그인 연장] axios interceptors 사용하기

Mark·2022년 9월 17일
16
post-thumbnail

axios interceptors를 사용하는 이유?

지금 이해한 방법이 정확한 것은 아닙니다!! 프로젝트에 있어서 느낀 편리성에 대해 작성했습니다.

1. api 요청시 반복되는 부분을 줄일 수 있음

  • 내가 프로젝트를 진행하면서 느낀 바로는 꼭 axios interceptors를 사용해야 되는 것은 아니지만 예를들어, 배포된 서버에 api를 요청할 때 header에 필수적으로 들어가야하는 부분이 있을 경우, 매번 요청때마다 header에 값을 넣어서 처리하는 것이 아닌 interceptors를 사용해서 api 요청시 자동으로 해당 값이 들어가서 매 요청때마다 header에 값을 넣지 않아도 되고, 코드도 간결해지고 복잡성도 줄어든다는 장점이 있었다.

2. 특정 에러를 캐치해서 작업을 진행시켜야 할 경우

  • 이 부분에 대해서는 로그인 연장 처리부분이 큰 도움을 받았다.
  • interceptor를 사용하기 전에는 access token이 만료되는 것을 방지하기 위해서 페이지가 새로고침 될 때마다 access token을 발급받는 api를 호출했었는데, 이렇게 될 경우 이전 access token과 이후 access token이 달라서 access token이 필요한 api를 호출할 때 토큰 문제로 api에서 값을 받아오지 못하는 등의 문제들이 발생했다.
  • 기술 매니저님의 피드백과 구글링을 통해 axios interceptor를 사용해서 어떤 api를 호출할 때 access token이 만료돼서 데이터를 받아오지 못하는 문제가 발생했을 때 이를 미리 캐치해서(백엔드에서 설정한 에러 상태를 통해 캐치함) 새로운 access token을 발급받아서 access token 문제로 실행되지 못했던 api들이 호출될 수 있도록 해결할 수 있다.

axios interceptor 사용하기

1) 기본 세팅

  • baseURL을 생성하면 api 호출시 URL을 반복해서 입력하지 않아도 된다.
// api/userApis.js
const api = axios.create({
  baseURL: "url",
  headers: {
    "content-type": "application/json;charset=UTF-8",
    accept: "application/json,",
  },
  withCredentials: true,
});

2) request 설정하기

  • 아래 코드를 설정하면 header에 token이 필요한 api를 호출할 경우 자동으로 header에 값이 들어가게 된다.
// api/userApis.js

const access_token = getCookie("access_token");
const refresh_token = getCookie("refresh_token");
// api/userApis.js

api.interceptors.request.use(function (config) {
  config.headers.common["Authorization"] = access_token;
  config.headers.common["Refresh-Token"] = refresh_token;
  return config;
});

3) api 호출하는 함수 작성

  • 위에서 설정한 api에 method를 포함시켜서 간결하게 호출할 수 있게 된다.
// api/userApis.js

export const userApis = {
  // member
  doubleCheck: (data) => {
    return api.post("member/email", {
      email: data,
    });
  },
  signup: async (data) => {
    const response = await api.post("member/signup", data);
    return response.data;
  },
}

4) api 호출하기

  • 요청이 필요한 곳에 useApis.signup 이런 식으로 간편하게 호출할 수 있다.
// src/components/register/Register.jsx

const onSubmit = () => {
      userApis
        .signup(userData)
        .then((res) => {
          alert("회원가입 완료!");
          navigate(`/`);
        })
        .catch((error) => {
          if (error.response.data.msg === "중복된 닉네임이 있습니다.") {
            alert("중복된 닉네임입니다!");
          }
      });
  };

로그인 연장 처리하기

1. interceptors response 추가하기

  • interceptors.response.use를 추가한다.
  • 백엔드에서 맞춰준 토큰 만료시 발생하는 에러 status에 따라 access token을 새로 발급시키는 함수(refreshAccessToken)를 실행시킨다.
  • 하지만 이렇게 처리하게 되면 403 에러가 캐치될 때 access token이 계속 발급되는 무한 로딩 현상이 발생한다.
// api/userApi.js

api.interceptors.response.use(
  function (response) {
    return response;
  },
  async function (err) {
    const originalConfig = err.config;
    if (err.response && err.response.data.status === "403 FORBIDDEN") {
      try {
        const refreshToken = await getCookie("refresh_token");
        axios.defaults.headers.common["refresh-token"] = refreshToken;
        refreshAccessToken();
        return api.request(originalConfig);
      } catch (err) {
        console.log("error", err.response);
        window.location.href = "/";
      }
     }
      return Promise.reject(err);
    }
    return Promise.reject(err);
  }
);

const refreshAccessToken = async () => {
  const response = await axios.post("http://localhost:8080/member/reissue");
  const access_token = response.headers["authorization"];
  setCookie("access_token", access_token); 
  window.location.reload();
};

2. 무한 로딩 문제 해결하기

1) flag 추가하기 (isTokenRefresh라는 변수 생성)

  • 구글링을 통해서 true/false로 값을 받는 isTokenRefresh라는 변수를 생성한 flag를 추가하니까 무한 로딩이 해결은 되었다.
  • 하지만 flag를 주는 것도 맞는 방법이 아닌 어쩌다 해결한 느낌이라서 찝찝함이 있었다.
let isTokenRefresh = false;

api.interceptors.response.use(
  function (response) {
    return response;
  },
  async function (err) {
    const originalConfig = err.config;
    if (err.response && err.response.data.status === "403 FORBIDDEN") {
      if (!isTokenRefresh) {
      isTokenRefresh = true;
      try {
        const refreshToken = await getCookie("refresh_token");
        axios.defaults.headers.common["refresh-token"] = refreshToken;
        refreshAccessToken();
	    return api.request(originalConfig);
      } catch (err) {
        console.log("error", err.response);
        window.location.href = "/";
      }
     }
      return Promise.reject(err);
    }
    return Promise.reject(err);
  }
);

2) flag를 추가하지 않고 문제 해결

  • 이렇게 구현한 부분에 대해서 기술 매니저님에게 피드백을 요청했다.
  • 기술 매니저님도 원래는 flag를 추가하지 않아도 무한 로딩이 되지 않는게 맞다고 말씀해주셨고 isTokenRefresh 변수를 다시 삭제하고, api.request(originalConfig) 이 부분도 삭제해서 다시 확인해보라는 해결책을 주셨다. 기술 매니저님이 말씀해주신대로 구현하니까 무한 로딩 문제가 해결되었다!
  • interceptors에 대한 이해가 완벽하지 않아서 정확한 문제 원인은 지금도 잘 모르겠다ㅠㅠ 공부가 더 필요하다..!!
  • 아래는 해결된 코드이다.(정확한 코드는 아니다…)
api.interceptors.response.use(
  function (response) {
    return response;
  },
  async function (err) {
    const originalConfig = err.config;
    if (err.response && err.response.data.status === "403 FORBIDDEN") {
      try {
        // 기존에 쿠키에 저장된 refresh token을 가져옴
		// refresh token만 가지고 access token 발급을 요청할 수 있도록 백엔드 팀원분들에게 요청 후 api를 설정함
        const refreshToken = await getCookie("refresh_token");
        axios.defaults.headers.common["refresh-token"] = refreshToken;
		// 토큰을 다시 발급 받는 api 호출 함수 
        refreshAccessToken();
      } catch (err) {
        console.log("error", err.response);
        window.location.href = "/";
      }
      return Promise.reject(err);
    }
    return Promise.reject(err);
  }
);

const refreshAccessToken = async () => {
  const response = await axios.post("http://localhost:8080/member/reissue");
	// response를 받고 header부분에 token을 받아서 쿠키에 담기 
  const access_token = response.headers["authorization"];
  setCookie("access_token", access_token); 
	// 화면에 바로 반영이 안돼서 강제적으로 reload 시킴 
  window.location.reload();
};
profile
개인 공부 정리

0개의 댓글