⛵ 항해99/TIL · WIL ✏️

[TIL] 2023.11.13 - 트러블 슈팅(Token 재발급 후 페이지 이동 이슈)

hhhhy 2023. 11. 20. 15:10

 트러블 슈팅(Token 재발급 후 페이지 이동 이슈) 

① 문제

  • axios interceptors를 사용해 axios가 서버로 HTTP 요청을 보내기 전, refresh Token으로 만료된 access Token을 재발급 받도로록 구현함.
  • try catch문을 사용해 에러가 발생했을 때는 쿠키에 저장된 refresh Token과 로컬스토리지에 저장된 access Token을 모두 지우고 로그인 화면으로 이동하도록 useNavigate를 사용했는데, 페이지 이동이 되지 않는 이슈가 발생함.

② 시도

1) 토큰 재발급 코드 위치 변경 : axios interceptors → Layout

  • axios interceptors는 useNavigate가 실행되지 않아 모든 페이지에 영향을 주는 Layout으로 옮겨서 Token 재발급 코드를 작성함

2) Layout 이동 후 : useNavigate는 동작하나, 토큰 재발급을 후 렌더링 이슈 발생

  • 코드 위치를 바꾸고 나서 useNavigate는 잘 동작하지만, 각 페이지가 바로 렌더링되지 않고, 새로고침을 눌러야 렌더링이 되는 문제가 발생함.

③ 해결

  • 리액트 훅(useNavigate, useEffect 등등)은 jsx파일이거나, 커스텀훅(use000.jsx) 파일에서만 쓸 수 있었음.
  • interceptors는 js 파일이기때문에 useNavigate를 사용하는 대신에 window.location.href = "/login";로 수정해 문제를 해결함.
// accessToken 재발급
axiosInstance.interceptors.response.use(
  (response) => {
    return response;
  },
  async (error) => {
    // 액세스 토큰 만료됐을 때,
    if (error.response.data.status === 500) {
      const refreshToken = getCookie("refreshToken"); // 리프레시 토큰 가져오고,
      localStorage.removeItem("accessToken"); // 엑세스 토큰은 지우기(axiosInstance 기본설정 때문에 post 보낼때 같이 보내지는데, 403 에러남!)

      console.log("액세스 토큰 만료", error);
      console.log("refreshToken 있니?", refreshToken);

      // 1. 리프레시 토큰이 있으면,
      if (refreshToken) {
        try {
          // 재발급 API로 리프레시 토큰 보내서
          const response = await axiosInstance.post(
            "/api/token/refreshAccessToken",
            null, // 요청 body 보내지 않음
            {
              headers: {
                Authorization_refresh: refreshToken,
              },
            }
          );
          console.log("리프레시 있을 때 response", response);

          // 새로운 토큰(액세스/리프레시) 받고, 저장하기
          const newAccessToken = response.data.accessToken;
          const newRefreshToken = response.data.refreshToken;
          localStorage.setItem("accessToken", newAccessToken);
          setCookie("refreshToken", newRefreshToken, {
            path: "/", // 모든 페이지에서 쿠키 접근 가능
            secure: true, // https로 통신할때만 쿠키 저장됨
            // httpOnly: true, // HttpOnly 속성을 적용(js 접근 불가) > 클라이언트에서 저장안됨
          });

          error.config.headers["Authorization"] = newAccessToken; // 업데이트된 액세스 토큰을 헤더에 설정하고 현재 요청을 다시 실행
          return axiosInstance(error.config); // 현재 요청을 새로운 액세스 토큰을 가지고 재시도

          // 2. 리프레시 토큰 에러면,
        } catch (refreshError) {
          console.log("리프레시 에러 refreshError", refreshError);

          // 토큰(액세스/리프레시) 지우고, 재로그인 시키기
          localStorage.removeItem("accessToken");
          removeCookie("refreshToken");
          window.location.href = "/login";
          alert("재로그인이 필요합니다.");
        }
      } else {
        // 토큰(액세스/리프레시) 지우고, 재로그인 시키기
        localStorage.removeItem("accessToken");
        removeCookie("refreshToken");
        window.location.href = "/login";
        alert("재로그인이 필요합니다.");
      }
    }

    // 다른 오류의 경우, 오류를 다시 반환
    return Promise.reject(error);
  }
);