axios/ forEach vs for of

Hunter Joe·2026년 3월 19일
import { ENV } from '@/configs/env';
import useAuthStore from '@/store/use-auth-store';
import axios from 'axios';

type FailedQueue = Array<{
  resolve: (token: string | null) => void;
  reject: (error: unknown) => void;
}>;

export const apiCall = axios.create({
  baseURL: ENV.NEXT_PUBLIC_SERVER_URL,
  withCredentials: true,
});

apiCall.interceptors.request.use((config) => {
  const { accessToken } = useAuthStore.getState();
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`;
  }
  return config;
});

let isRefreshing = false;
let failedQueue: FailedQueue = [];

const processQueue = (error: unknown, token: string | null = null) => {
  for (const { resolve, reject } of failedQueue) {
    if (error) {
      reject(error);
    } else {
      resolve(token);
    }
  }
  failedQueue = [];
};

apiCall.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status !== 401 || originalRequest._retry || originalRequest.url === '/auth/refresh') {
      throw error;
    }

    if (isRefreshing) {
      return new Promise((resolve, reject) => {
        failedQueue.push({ resolve, reject });
      }).then((token) => {
        originalRequest.headers.Authorization = `Bearer ${token}`;
        return apiCall(originalRequest);
      });
    }

    originalRequest._retry = true;
    isRefreshing = true;

    try {
      const newToken = await useAuthStore.getState().refreshAccessToken();
      if (!newToken) {
        processQueue(new Error('Refresh failed'));
        throw error;
      }

      processQueue(null, newToken);
      originalRequest.headers.Authorization = `Bearer ${newToken}`;
      return apiCall(originalRequest);
    } catch (refreshError) {
      processQueue(refreshError);
      throw refreshError;
    } finally {
      isRefreshing = false;
    }
  },
);
profile
Improvise, Adapt, Overcome

0개의 댓글