목적은?

API 요청시 토큰 만료로 인한 401 에러가 발생할 경우를 대응하기 용도입니다.

방법

  1. API 요청에서 401 에러가 발생하면, 해당 요청을 대기 큐에 저장합니다.
  2. 만약 이미 refreshToken 재발급 중이면, 추가로 실패하는 API들도 대기 큐에 계속 저장합니다.
  3. 재발급이 성공하면, 대기 큐에 쌓인 모든 API를 재요청합니다.
  4. 재요청이 성공하면 정상 응답, 실패하면 에러를 반환합니다.
  5. 만약 refreshToken 재발급 자체가 실패하면, 대기 큐의 모든 요청에 대해 에러를 반환합니다.
graph LR
    A[API 요청] --> B{401 에러 발생?}
    B -- 아니오 --> Z[정상 응답]
    B -- 예 --> C[실패한 API 대기 큐에 저장]
    C --> D{refreshToken 재발급 중?}
    D -- 아니오 --> E[refreshToken으로 토큰 재발급 요청]
    E --> F{재발급 성공?}
    F -- 예 --> G[대기 큐의 모든 API 재요청]
    G --> H{재요청 성공?}
    H -- 예 --> Z
    H -- 아니오 --> I[에러 반환]
    F -- 아니오 --> I
    D -- 예 --> J[대기 큐에 계속 저장]
    J --> E
api.interceptors.response.use(
  (response) => response, // 정상 응답은 그대로
  async (error) => {
    // 401 에러 시 토큰 재발급 로직
    const originalRequest = error.config;
    if (
      error.response &&
      error.response.status === 401 &&
      !originalRequest._retry
    ) {
      originalRequest._retry = true;
      try {
        // 토큰 재발급 요청
        const res = await axios.post('/auth/refresh', {
          refreshToken: localStorage.getItem('refreshToken'),
        });
        const newToken = res.data.accessToken;
        localStorage.setItem('accessToken', newToken);

        // 원래 요청에 새 토큰 적용
        originalRequest.headers.Authorization = `Bearer ${newToken}`;
        return api(originalRequest); // 재요청
      } catch (refreshError) {
        // 재발급 실패 시 처리 (예: 로그아웃)
        return Promise.reject(refreshError);
      }
    }
    // 기타 에러 처리
    return Promise.reject(error);
  }
);

[originalRequest._retry 용도]

“두 번째로 같은 요청이 실패" 하는 경우

토큰 재발급이 실패하거나 재발급된 토큰이 유효하지 않거나서버에서 refresh token도 만료된 상태라면 → 재요청(두 번째 요청)도 401 에러가 발생할 수 있습니다.

만약 _retry 플래그가 없다면, 401 → 재발급 → 재요청 → 또 401 → 또 재발급 흐름으로 무한 루프가 발생할 수 있습니다.

[재발급 완료 후 재요청 과정에서 헤더에 다시 토큰을 업데이트하는 이유]