TIL

JS

axios interceptor

TIL을 작성하기 전에 이미 axios interceptor를 구현해서 적을 일이 없을 것 같았는데,
콘솔에 찍히는 에러 로그를 최대한 줄이기 위해 디버깅을 진행하다보니 axios interceptor도 업데이트하게 되었다.

기존에는 전역 변수로 엑세스 토큰을 저장하고,
request에서는 이 토큰에 값이 있는 경우(!null)에 config의 header에 토큰을 세팅하는 것만 있고,
response에 대한 intercept를 중점으로 구현했다.

상태코드로 403을 받았을 때, 엑세스토큰 재발급 api를 요청하고,
이 api 결과도 403이면 로그아웃을, 200인 경우 엑세스토큰을 다시 변수에 세팅한 뒤, 실패한 config를 재요청하는 방식이다.

이 경우도 문제 없이 동작하지만, 새로고침하여 변수의 토큰을 잃게된 상태로 api 요청을 하게 되는 경우 초기 요청이 모두 403을 받아 콘솔에 에러 로그가 찍히게 된다.
이를 막기 위해 request에도 요청 전 엑세스 토큰이 null인 경우 재발급 요청을 하고 결과에 따라 로그아웃-엑세스토큰 세팅을 거치게 된다.

따라서 중복되는 로직인 (재발급 요청 -> 로그아웃   엑세스토큰 세팅)도 하나의 함수로 분리했다.

이 때 또 절대 잊지 말아야 하는 것..!
내가 또 같은 실수를 반복한 것..!

axios interceptor 내에서 요청하는 axios 요청도 다시 interceptor에 잡힌다는 것을 잊지 말자!

때문에 조건에 따라 interceptor를 벗어나지 못하고 무한 루프에 빠지게 될 수 있다.

response interceptor를 구현할 때 이 때문에 헤맸었는데, 그 기억덕에 request interceptor에서는 덜 헤맬 수 있었다.

코드로 흐름만 정리하자면,

let accessToken = null;

function setAccessToken() {
  // 전역 변수에 엑세스 토큰 저장
}

async function onAccessTokenExpired() {
  // accessToken 재발급 요청
  const result = await axios.get("/access");
  if (result === "REFRESH EXPIRE")
    signout();
  else
    setAccessToken();
}

function requestInterceptor(config) {
  if (!accessToken && config.url !== "/access")
    onAccessTokenExpired();
  config.headers.Authorization = `Bearer ${accessToken}`;
  return config;
}

( ...생략 )

accessToken 재발급을 하는 요청에서는 리프레시 토큰만 필요하므로 헤더 설정이 필요없다.
따라서 조건으로 config.url !== "/access"를 주어야 “access” 요청을 무한정 하지 않을 수 있다.
아니면 재발급 요청은 interceptor가 설정되지 않은 다른 instance를 사용하는 방법도 있을 것 같다.

이렇게 request를 업데이트해서 요청 전 엑세스 토큰을 잃어버린 경우의 에러 로그를 줄일 수 있었다.
하지만 새로고침 이외에 엑세스 토큰을 잃지는 않았지만, 실제로 만료가 된 경우에는 에러 로그를 막을 수 없다.
request에서 미리 만료를 확인하는 방법이나 엑세스 토큰을 디코딩해서 만료일을 비교하는 방법도 있겠지만,
리소스 낭비일 것 같아서 이 정도로 업데이트를 마친다.

다른 사람들은 interceptor를 어느 정도까지 구현해서 사용하고 있는지도 궁금하네…!