- Access 토큰 만료시 Refresh 토큰으로 Access 토큰 재갱신 요청 로직 구현 중
This commit is contained in:
@@ -6,10 +6,15 @@ import type {
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
} from "axios";
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { RefreshAccessTokenResponse } from '@/data/response/account/RefreshAccessTokenResponse';
|
||||
|
||||
export class BaseNetwork {
|
||||
protected instance: AxiosInstance;
|
||||
|
||||
private isRefreshing = false;
|
||||
private refreshQueue: Array<(token:string) => void> = [];
|
||||
|
||||
constructor() {
|
||||
this.instance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || "http://localhost:3000",
|
||||
@@ -48,20 +53,86 @@ export class BaseNetwork {
|
||||
this.instance.interceptors.response.use(
|
||||
(response: AxiosResponse) => response,
|
||||
(error: AxiosError) => {
|
||||
const message =
|
||||
(error.response?.data as any)?.message ||
|
||||
error.message ||
|
||||
"Unknown error";
|
||||
const status = error.response?.status;
|
||||
const errorCode = (error.response?.data as any)?.code;
|
||||
const originalRequest = error.config as AxiosRequestConfig & {
|
||||
_retry?: boolean;
|
||||
}
|
||||
|
||||
return Promise.reject({
|
||||
status: error.response?.status,
|
||||
message,
|
||||
raw: error,
|
||||
});
|
||||
if (
|
||||
status === 401
|
||||
&& errorCode === 'AccessTokenExpired'
|
||||
&& !originalRequest._retry
|
||||
) {
|
||||
originalRequest._retry = true;
|
||||
return this.handleRefreshToken(originalRequest);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async handleRefreshToken(originalRequest: AxiosRequestConfig) {
|
||||
|
||||
const authData = useAuthStore.getState().authData;
|
||||
const refreshToken = authData?.refreshToken;
|
||||
|
||||
if (!authData || !refreshToken) {
|
||||
useAuthStore.getState().logout();
|
||||
return Promise.reject("no refresh token");
|
||||
}
|
||||
|
||||
if (this.isRefreshing) {
|
||||
return new Promise((resolve) => {
|
||||
this.refreshQueue.push((newToken: string) => {
|
||||
originalRequest.headers = {
|
||||
...originalRequest.headers,
|
||||
Authorization: `Bearer ${newToken}`
|
||||
};
|
||||
resolve(this.instance(originalRequest));
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.isRefreshing = true;
|
||||
|
||||
try {
|
||||
const response = await this.get<RefreshAccessTokenResponse>(
|
||||
'/account/refresh-access-token',
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${refreshToken}`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const newAccessToken = response.data.accessToken;
|
||||
const newRefreshToken = response.data.refreshToken;
|
||||
|
||||
useAuthStore.getState().login({
|
||||
...authData,
|
||||
accessToken: newAccessToken,
|
||||
refreshToken: newRefreshToken
|
||||
});
|
||||
|
||||
this.refreshQueue.forEach((cb) => cb(newAccessToken));
|
||||
this.refreshQueue = [];
|
||||
originalRequest.headers = {
|
||||
...originalRequest.headers,
|
||||
Authorization: `Bearer ${newAccessToken}`,
|
||||
};
|
||||
|
||||
return this.instance(originalRequest);
|
||||
} catch (err) {
|
||||
useAuthStore.getState().logout();
|
||||
window.location.href = '/login?expired=1'
|
||||
return Promise.reject(err);
|
||||
} finally {
|
||||
this.isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 CRUD 메서드
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user