diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts index 1a84d87..fee8b37 100644 --- a/src/services/apiClient.ts +++ b/src/services/apiClient.ts @@ -2,7 +2,6 @@ import axios from "axios"; import { getAccessToken, removeAccessToken } from "@/utils/tokenUtils"; import authService from "./authService"; import router from "next/router"; -import useAuthStore from "@/stores/useAuthStore"; const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, @@ -11,40 +10,73 @@ const apiClient = axios.create({ }, }); -// request +let isRefreshing = false; +let failedQueue: Array<{ + resolve: (token: string) => void; + reject: (error: any) => void; +}> = []; + +const processQueue = (error: any, token: string | null = null) => { + failedQueue.forEach(prom => { + if (error) { + prom.reject(error); + } else if (token) { + prom.resolve(token); + } + }); + failedQueue = []; +}; + apiClient.interceptors.request.use( - (config) => { - const accessToken = getAccessToken(); - if (accessToken) { - config.headers["Authorization"] = `Bearer ${accessToken}`; + async (config) => { + const token = getAccessToken(); + if (token) { + config.headers["Authorization"] = `Bearer ${token}`; } return config; }, - (error) => { - return Promise.reject(error); - }, + (error) => Promise.reject(error) ); -// response apiClient.interceptors.response.use( (response) => response, async (error) => { - if (error.response && error.response?.status === 401) { + const originalRequest = error.config; + + // 기존 로직 유지 + if (error.response?.status === 401 && !originalRequest._retry) { + if (isRefreshing) { // 동시 요청 들어올 시, 중복으로 refreshToken 요청 방지 + try { + const token = await new Promise((resolve, reject) => { + failedQueue.push({ resolve, reject }); // 이때는 아직 토큰이 재발급 전이니 클라이언트 요청 대기열에 추가 + }); + originalRequest.headers["Authorization"] = `Bearer ${token}`; + return apiClient(originalRequest); // 원래 본 요청 처리 + } catch (err) { + return Promise.reject(err); + } + } + + originalRequest._retry = true; + isRefreshing = true; + try { - const newAccessToken = await authService.refreshToken(); - alert("새 토큰 발급!"); - error.config.headers["Authorization"] = `Bearer ${newAccessToken}`; - return apiClient(error.config); - } catch (error: any) { - console.error("토큰 갱신 실패", error); + const token = await authService.refreshToken(); + originalRequest.headers["Authorization"] = `Bearer ${token}`; + processQueue(null, token); // 기존 대기열에 쌓여있던 요청들에게 토큰 전달 + return apiClient(originalRequest); // 원래 본 요청 처리 + } catch (refreshError) { + processQueue(refreshError, null); removeAccessToken(); - router.push("/login"); - alert("새로 로그인해주세요!"); - return Promise.reject(error); + router.push("/login"); // 토큰 재발급 실패 했을때 처리 필요함 + return Promise.reject(refreshError); + } finally { + isRefreshing = false; } } + return Promise.reject(error); - }, + } ); export default apiClient;