Refactor: axios 컨피그 리팩토링, 그러나 덜 됐음.

This commit is contained in:
정현조 2024-09-18 16:54:39 +09:00
parent c547860f62
commit 1547e93a84

View File

@ -1,32 +1,27 @@
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import useAuthStore from '@/stores/useAuthStore'; import useAuthStore from '@/stores/useAuthStore';
import { BaseResponse, CustomError, SuccessResponse, RefreshTokenResponse } from '@/types'; import { RefreshTokenResponse, ErrorResponse } from '../types';
const baseURL = import.meta.env.VITE_API_URL;
const api = axios.create({ const api = axios.create({
baseURL, baseURL: `${import.meta.env.VITE_API_URL}/api`,
withCredentials: true, withCredentials: true,
}); });
let isTokenRefreshing = false; const refreshApi = axios.create({
baseURL: `${import.meta.env.VITE_API_URL}/api`,
withCredentials: true,
});
type FailedRequest = { let isRefreshing = false;
resolve: (value?: string | undefined) => void; let refreshSubscribers: Array<(token: string) => void> = [];
reject: (reason?: unknown) => void;
const subscribeTokenRefresh = (cb: (token: string) => void) => {
refreshSubscribers.push(cb);
}; };
let failedQueue: FailedRequest[] = []; const onRefreshed = (token: string) => {
refreshSubscribers.forEach((cb) => cb(token));
const processQueue = (error: Error | null, token: string | undefined = undefined): void => { refreshSubscribers = [];
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
}; };
api.interceptors.request.use((config: InternalAxiosRequestConfig) => { api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
@ -39,68 +34,45 @@ api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
api.interceptors.response.use( api.interceptors.response.use(
(response: AxiosResponse) => response, (response: AxiosResponse) => response,
async (error: AxiosError<BaseResponse<CustomError>>) => { (error: AxiosError<ErrorResponse>) => {
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }; const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
if (error.response?.status === 401 && !originalRequest._retry) { if (error.response?.status === 401 && !originalRequest._retry) {
if (isTokenRefreshing) {
return new Promise<string | undefined>((resolve, reject) => {
failedQueue.push({ resolve, reject });
})
.then((token) => {
if (token && originalRequest.headers) {
originalRequest.headers.Authorization = `Bearer ${token}`;
}
return api(originalRequest);
})
.catch((err) => Promise.reject(err));
}
originalRequest._retry = true; originalRequest._retry = true;
isTokenRefreshing = true;
try { if (isRefreshing) {
const response: AxiosResponse<SuccessResponse<RefreshTokenResponse>> = await api.post('/auth/reissue', null, { return new Promise<void>((resolve) => {
withCredentials: true, subscribeTokenRefresh((token: string) => {
originalRequest.headers.Authorization = `Bearer ${token}`;
resolve(api(originalRequest));
});
}); });
const newAccessToken = response.data.data?.accessToken;
if (!newAccessToken) {
throw new Error('Invalid token reissue response');
}
useAuthStore.getState().setLoggedIn(true, newAccessToken);
processQueue(null, newAccessToken);
const redirectUri = `/redirect/oauth2?accessToken=${newAccessToken}`;
window.location.href = redirectUri;
return Promise.reject(new Error('Redirecting to retrieve cookies'));
} catch (reissueError: unknown) {
processQueue(reissueError as Error, undefined);
console.error('토큰 재발급 실패:', reissueError);
useAuthStore.getState().clearAuth();
window.location.href = '/';
return Promise.reject(reissueError);
} finally {
isTokenRefreshing = false;
} }
isRefreshing = true;
return refreshApi
.post<RefreshTokenResponse>('/auth/reissue', null, { withCredentials: true })
.then(({ data }) => {
const newAccessToken = data.accessToken;
useAuthStore.getState().setLoggedIn(true, newAccessToken);
onRefreshed(newAccessToken);
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return api(originalRequest);
})
.catch(() => {
useAuthStore.getState().clearAuth();
console.log('리이슈 실패');
window.location.href = '/';
return Promise.reject(error);
})
.finally(() => {
isRefreshing = false;
});
} }
handleCommonErrors(error);
return Promise.reject(error); return Promise.reject(error);
} }
); );
const handleCommonErrors = (error: AxiosError<BaseResponse<CustomError>>) => {
if (error.response?.status === 400) {
alert('잘못된 요청입니다. 다시 시도해 주세요.');
} else if (error.response?.status === 403) {
alert(error.response?.data?.message);
} else {
console.error('오류 발생:', error.response?.data?.message || '알 수 없는 오류');
}
};
export default api; export default api;