Skip to content

Commit

Permalink
refactor : 에러 처리 로직 수정 & 대기 배열 관련 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
cmlim0070 committed Jan 14, 2025
1 parent 2c5689f commit 4a5211d
Showing 1 changed file with 71 additions and 54 deletions.
125 changes: 71 additions & 54 deletions src/service/api.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import axios, { AxiosRequestConfig, AxiosInstance } from 'axios';
import axios, { AxiosRequestConfig, AxiosInstance, AxiosError } from 'axios';
import { tokenStorage } from './auth/tokenStorage';
import { refreshAccessToken } from '@/service/auth/refreshAccessToken';
import { logout } from './auth/logout';
import { formatApiError } from '@/util/formatApiError';

interface QueueItem {
resolve: (token: string) => void;
reject: (error: QueueError) => void;
}

type QueueError = AxiosError | Error;

const baseUrl = import.meta.env.VITE_API_URL as string;

let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];

// TODO - 대기중인 요청 처리 함수 구현
// . . .
let requestQueue: QueueItem[] = [];
const processQueue = (error: QueueError | null, token: string | null): void => {
requestQueue.forEach((promise: QueueItem) => {
if (error) {
promise.reject(error);
} else if (token) {
promise.resolve(token);
}
});
requestQueue = [];
};

export const defaultApi = (option?: AxiosRequestConfig): AxiosInstance => {
const instance = axios.create({
Expand All @@ -19,6 +33,7 @@ export const defaultApi = (option?: AxiosRequestConfig): AxiosInstance => {
...option
});

// 요청 인터셉터
instance.interceptors.request.use(
function (config) {
const accessToken = tokenStorage.getAccessToken();
Expand All @@ -32,65 +47,58 @@ export const defaultApi = (option?: AxiosRequestConfig): AxiosInstance => {
}
);

// 액세스 토큰 만료됨
// 응답 인터셉터
instance.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
console.error('에러:', error);

const originalRequest = error.config;

if (error.response?.status === 401 && !originalRequest._retry) {
if (!isRefreshing) {
isRefreshing = true;
originalRequest._retry = true;
// 갱신 중
if (isRefreshing) {
return new Promise((resolve, reject) => {
requestQueue.push({
resolve: (token: string) => {
originalRequest.headers['Authorization'] =
`Bearer ${token}`;
resolve(instance(originalRequest));
},
reject
});
});
}

try {
// 액세스토큰 재발급 완료
const refreshAccessTokenResponse =
await refreshAccessToken();
isRefreshing = true;
originalRequest._retry = true;

if (refreshAccessTokenResponse.isSuccess) {
console.log('액세스 토큰 재발급됨');
const newAccessToken =
refreshAccessTokenResponse.result
.newAccessToken;
console.log(newAccessToken);
tokenStorage.setAccessToken(newAccessToken);
try {
const response = await refreshAccessToken();

originalRequest.headers['Authorization'] =
`Bearer ${newAccessToken}`;
if (response.isSuccess && response.result.newAccessToken) {
const newAccessToken = response.result.newAccessToken;
tokenStorage.setAccessToken(newAccessToken);

// 대기 요청에 토큰 전달
refreshSubscribers.forEach((callback) =>
callback(newAccessToken)
);
originalRequest.headers['Authorization'] =
`Bearer ${newAccessToken}`;

refreshSubscribers = [];
return instance(originalRequest);
}
// 리프레시 토큰 만료
logout();
window.location.href = '/login';
return Promise.reject(error);
} catch (error) {
// 네트워크 에러?
return Promise.reject(error);
} finally {
isRefreshing = false;
processQueue(null, newAccessToken);
return instance(originalRequest);
}
logout();
window.location.href = '/login';
return Promise.reject(error);
} catch (error) {
processQueue(
new Error('로그인 토큰이 만료되었습니다.'),
null
);
logout();
window.location.href = '/login';
return Promise.reject(error);
} finally {
isRefreshing = false;
}

// 대기 요청
return new Promise((resolve) => {
refreshSubscribers.push((token: string) => {
originalRequest.headers['Authorization'] =
`Bearer ${token}`;
resolve(instance(originalRequest));
});
});
}
return Promise.reject(error);
}
Expand All @@ -104,12 +112,21 @@ export const defaultApi = (option?: AxiosRequestConfig): AxiosInstance => {
return response;
},
(error) => {
console.error('에러:', error);
if (error)
if (error.response?.data) {
throw formatApiError(
error.response.data.code,
error.response.data.message
);
} else if (!error.response) {
throw formatApiError(
'ERROR500',
'네트워크 요청에 실패했습니다.'
'NETWORK_ERROR',
'서버와의 통신에 실패했습니다.'
);
}
throw formatApiError(
'UNKNOWN_ERROR',
'요청 처리 중 문제가 발생했습니다.'
);
}
);

Expand Down

0 comments on commit 4a5211d

Please sign in to comment.