@@ -28,57 +28,57 @@ instance.interceptors.request.use(
2828 } ,
2929) ;
3030
31- let isAlertShown = false ;
31+ let tokenRefreshPromise : Promise < void > | null = null ;
32+ let isLoginAlertShown = false ;
3233
3334// 응답 인터셉터
3435instance . interceptors . response . use (
3536 ( response : AxiosResponse ) => response ?. data ,
3637 async ( error : AxiosError ) => {
3738 if ( typeof window === "undefined" ) {
3839 // Server에서는 기본 전파
39- return Promise . reject ( error ) ;
40- }
41-
42- if ( error . response ?. status === ERROR_CODE . SERVER_ERROR ) {
43- alert ( "서버에러가 발생하였습니다. 관리자에게 문의해주세요." ) ;
40+ console . error ( error ) ;
4441
45- return ;
46- }
47-
48- if ( error . config ?. url === API_URL . USER . REISSUE_ACCESS_TOKEN ) {
4942 return Promise . reject ( error ) ;
5043 }
5144
45+ console . error ( "##error" , JSON . stringify ( error ) ) ;
46+
5247 if ( isAxiosErrorResponse ( error . response ?. data ) ) {
5348 // 엑세스 토큰 없음
54- if ( error . response ?. data . code === ERROR_CODE . NO_ACCESS_TOKEN ) {
55- alert ( "로그인이 필요한 페이지입니다." ) ;
49+ if (
50+ error . response ?. data . code === ERROR_CODE . NO_ACCESS_TOKEN ||
51+ error . response ?. data . code === ERROR_CODE . ACCESS_TOKEN_EXPIRED ||
52+ error . response ?. data . code === ERROR_CODE . REFRESH_TOKEN_EXPIRED
53+ ) {
54+ redirectToLoginOnce ( ) ;
55+ }
5656
57- safeLocalStorage . remove ( IS_LOGINED ) ;
58- window . location . href = EXTERNAL_PATH . LOGIN ;
57+ if ( error ?. response ?. data . code === ERROR_CODE . NO_ACCESS ) {
58+ alert ( "접근 권한이 필요한 페이지입니다." ) ;
5959
60- return ;
60+ window . location . href = EXTERNAL_PATH . HOME ;
6161 }
6262
6363 // 액세스 토큰 만료
6464 if ( error . response ?. data . code === ERROR_CODE . ACCESS_TOKEN_EXPIRED ) {
6565 try {
66- if ( isAlertShown ) {
67- // 원래 요청 재시도
68- const originalRequest = error . config ;
69-
70- if ( ! originalRequest ) {
71- return Promise . reject ( error ) ;
72- }
73-
74- return instance ( originalRequest ) ;
66+ // 이미 토큰 재발급이 진행 중이면 완료될 때까지 대기
67+ if ( tokenRefreshPromise ) {
68+ await tokenRefreshPromise ;
69+ } else {
70+ // 토큰 재발급 시작
71+ tokenRefreshPromise = refreshAccessToken ( )
72+ . then ( ( ) => {
73+ tokenRefreshPromise = null ;
74+ } )
75+ . catch ( ( error ) => {
76+ tokenRefreshPromise = null ;
77+
78+ throw error ;
79+ } ) ;
7580 }
7681
77- // 엑세스 토큰 재발급
78- isAlertShown = true ;
79- await refreshAccessToken ( ) ;
80- isAlertShown = false ;
81-
8282 // 원래 요청 재시도
8383 const originalRequest = error . config ;
8484
@@ -87,34 +87,30 @@ instance.interceptors.response.use(
8787 }
8888
8989 return instance ( originalRequest ) ;
90- } catch ( error ) {
91- console . log ( "@@refresh error" , error ) ;
92-
90+ } catch {
9391 if ( typeof window !== "undefined" ) {
94- alert ( "로그인이 필요한 페이지입니다." ) ;
95-
96- // 엑세스 토큰 재발급 실패시 로그인 페이지로 이동
97- safeLocalStorage . remove ( IS_LOGINED ) ;
92+ redirectToLoginOnce ( ) ;
9893
99- window . location . href = EXTERNAL_PATH . LOGIN ;
100- isAlertShown = false ;
94+ return Promise . reject ( error ?. response ?. data ) ;
10195 }
10296 }
10397 }
98+ }
10499
105- if ( error . response ?. data . code === ERROR_CODE . REFRESH_TOKEN_EXPIRED ) {
106- alert ( "로그인이 필요한 페이지입니다." ) ;
107-
108- safeLocalStorage . remove ( IS_LOGINED ) ;
109- window . location . href = "/login" ;
110- }
111-
112- if ( error . response ?. data . code === ERROR_CODE . NO_ACCESS ) {
113- alert ( "접근 권한이 없는 페이지입니다." ) ;
114- window . location . href = EXTERNAL_PATH . HOME ;
115- }
100+ if ( error . response ?. status === 500 ) {
101+ return Promise . reject ( error ?. response ?. data ) ;
116102 }
117103
118104 return Promise . reject ( error ?. response ?. data ) ;
119105 } ,
120106) ;
107+
108+ function redirectToLoginOnce ( ) {
109+ if ( isLoginAlertShown ) return ;
110+
111+ isLoginAlertShown = true ;
112+
113+ alert ( "로그인이 필요한 페이지입니다." ) ;
114+ safeLocalStorage . remove ( IS_LOGINED ) ;
115+ window . location . href = EXTERNAL_PATH . LOGIN ;
116+ }
0 commit comments