본문 바로가기
FE/react

[React] 리액트 refresh token 구현 (with. axios interceptor)

by s0ojin 2023. 3. 21.

 

 

 

 

 

Axios interceptor와 Refresh Token


axios 공식문서에 따르면, interceptor는 api 요청에서 then이나 catch로 처리되기 전에 응답을 가로챌 수 있도록 하는 것입니다.

 

프로덕트에서 로그인 시에만 가능한 행동들의 경우 header에 athorization : token 옵션을 함께 보내야합니다.

이때 토큰이 만료되면 서버에서 토큰 만료로인한 오류응답을 줍니다(저희는 401이 토큰 만료 오류입니다).

 

토큰 탈취 문제를 피하고자 엑세스토큰의 유효시간은 짧게 설정해두고, 리프레시토큰을 통해 엑세스토큰을 재발급받아야하는데, 서비스를 이용하는 유저입장에서 반복적으로 401에러를 보는 것은 굉장히 불편한 일입니다.

그래서 저희는 401에러가 나오면, 유저가 보지 못하게 중간에서  가로채 리프레시 토큰으로 새 엑세스토큰을 발급받아 진행중이던 요청을 완료할 것입니다.

 

이때 axios interceptor를 이용하게됩니다.
axios interceptor는 api요청 보낼 때 가로채는 axios.interceptors.request
api응답을 받을 때 가로채는 interceptor.response가 있습니다.

 

401 응답이 올때의 로직을 추가해야하므로, 아래 axios.interceptors.response.use()를 활용해야합니다.

// 요청 인터셉터 추가하기
axios.interceptors.request.use(function (config) {
    // 요청이 전달되기 전에 작업 수행
    return config;
  }, function (error) {
    // 요청 오류가 있는 작업 수행
    return Promise.reject(error);
  });

// 응답 인터셉터 추가하기
axios.interceptors.response.use(function (response) {
    // 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
    // 응답 데이터가 있는 작업 수행
    return response;
  }, function (error) {
    // 2xx 외의 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
    // 응답 오류가 있는 작업 수행
    return Promise.reject(error);
  });

 

코드


//토큰이 필요한 api요청을 보내는 axios인스턴스
export const privateApi = axios.create({
  baseURL: `${process.env.REACT_APP_SERVER_IP}`,
  headers: {
    Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
  },
});

//refresh token api
export async function postRefreshToken() {
  const response = await publicApi.post('/api/v1/auth/refresh', {
    refreshToken: localStorage.getItem('refreshToken'),
  });
  return response;
}

//토큰을 함께보내는 privateApi에 interceptor를 적용합니다
privateApi.interceptors.response.use(
  // 200번대 응답이 올때 처리
  (response) => {
    return response;
  },
  // 200번대 응답이 아닐 경우 처리
  async (error) => {
    const {
      config,
      response: { status },
    } = error;
    
	//토큰이 만료되을 때
    if (status === 401) {
      if (error.response.data.message === 'Unauthorized') {
        const originRequest = config;
        //리프레시 토큰 api
        const response = await postRefreshToken();
        //리프레시 토큰 요청이 성공할 때
        if (response.status === 200) {
          const newAccessToken = response.data.token;
          localStorage.setItem('accessToken', response.data.token);
          localStorage.setItem('refreshToken', response.data.refreshToken);
          axios.defaults.headers.common.Authorization = `Bearer ${newAccessToken}`;
          //진행중이던 요청 이어서하기
          originRequest.headers.Authorization = `Bearer ${newAccessToken}`;
          return axios(originRequest);
        //리프레시 토큰 요청이 실패할때(리프레시 토큰도 만료되었을때 = 재로그인 안내)
        } else if (response.status === 404) {
          alert(LOGIN.MESSAGE.EXPIRED);
          window.location.replace('/sign-in');
        } else {
          alert(LOGIN.MESSAGE.ETC);
        }
      }
    }
    return Promise.reject(error);
  },
);

 

 

다음 글


변경된 토큰이 제대로 갱신되지 않는 오류가 발생하여 다음 글을 작성하였습니다.

[React] axios interceptor header 토큰 갱신

 

 

 

 

 

댓글