
--- client/resources/api/index.js
+++ client/resources/api/index.js
... | ... | @@ -82,96 +82,114 @@ |
82 | 82 |
} |
83 | 83 |
}); |
84 | 84 |
|
85 |
- // 요청 인터셉터 |
|
86 |
- client.interceptors.request.use( |
|
87 |
- async config => { |
|
88 |
- // 요청 전에 토큰 만료 확인 및 필요시 갱신 |
|
89 |
- try { |
|
90 |
- await ensureValidToken(); |
|
91 |
- const token = store.state.authorization; |
|
92 |
- if (token) { |
|
93 |
- config.headers.Authorization = token; |
|
85 |
+ // 요청 인터셉터 등록 |
|
86 |
+ if (client.interceptors.request.handlers.length === 0) { |
|
87 |
+ client.interceptors.request.use( |
|
88 |
+ async config => { |
|
89 |
+ if (store.state.isHandlingSessionError) { |
|
90 |
+ return Promise.reject(new axios.Cancel('Error handling in progress')); |
|
94 | 91 |
} |
95 |
- } catch (error) { |
|
96 |
- // 토큰 갱신 실패 시 로그인 페이지로 이동 |
|
97 |
- if (!window.location.pathname.includes('/login')) { |
|
98 |
- const redirect = window.location.pathname + window.location.search; |
|
99 |
- sessionStorage.setItem("redirect", redirect); |
|
100 |
- alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
101 |
- store.commit("setStoreReset"); |
|
102 |
- window.location = '/login.page'; |
|
92 |
+ try { |
|
93 |
+ await ensureValidToken(); |
|
94 |
+ const token = store.state.authorization; |
|
95 |
+ if (token) { |
|
96 |
+ config.headers.Authorization = token; |
|
97 |
+ } |
|
98 |
+ } catch (error) { |
|
99 |
+ if (!store.state.isHandlingSessionError && !window.location.pathname.includes('/login')) { |
|
100 |
+ store.commit('setHandlingSessionError', true); |
|
101 |
+ const redirect = window.location.pathname + window.location.search; |
|
102 |
+ sessionStorage.setItem("redirect", redirect); |
|
103 |
+ alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
104 |
+ store.commit("setStoreReset"); |
|
105 |
+ window.location = '/login.page'; |
|
106 |
+ } |
|
107 |
+ return Promise.reject(error); |
|
103 | 108 |
} |
104 |
- throw error; |
|
105 |
- } |
|
106 |
- return config; |
|
107 |
- }, |
|
108 |
- error => { |
|
109 |
- return Promise.reject(error); |
|
110 |
- } |
|
111 |
- ); |
|
109 |
+ return config; |
|
110 |
+ }, |
|
111 |
+ error => Promise.reject(error) |
|
112 |
+ ); |
|
113 |
+ } |
|
112 | 114 |
|
113 |
- // 응답 인터셉터 |
|
114 |
- client.interceptors.response.use( |
|
115 |
- response => { |
|
116 |
- return response; |
|
117 |
- }, |
|
118 |
- async error => { |
|
119 |
- const originalReq = error.config; |
|
115 |
+ // 응답 인터셉터 등록 |
|
116 |
+ if (client.interceptors.response.handlers.length === 0) { |
|
117 |
+ client.interceptors.response.use( |
|
118 |
+ response => response, |
|
119 |
+ async error => { |
|
120 |
+ if (store.state.isHandlingSessionError) { |
|
121 |
+ return Promise.reject(error); |
|
122 |
+ } |
|
120 | 123 |
|
121 |
- // 403 에러 처리 |
|
122 |
- if (error.response && error.response.status === 403) { |
|
123 |
- alert('접근 권한이 없습니다.'); |
|
124 |
- window.history.back(); |
|
124 |
+ const originalReq = error.config; |
|
125 |
+ |
|
126 |
+ if (error.response && error.response.status === 403) { |
|
127 |
+ if (!store.state.isHandlingSessionError) { |
|
128 |
+ store.commit('setHandlingSessionError', true); |
|
129 |
+ alert('접근 권한이 없습니다.'); |
|
130 |
+ window.history.back(); |
|
131 |
+ } |
|
132 |
+ return Promise.reject(error); |
|
133 |
+ } |
|
134 |
+ |
|
135 |
+ if ( |
|
136 |
+ error.response && |
|
137 |
+ error.response.status === 401 && |
|
138 |
+ error.response.data.message === '로그인 시간이 만료되었습니다.' && |
|
139 |
+ !originalReq._retry |
|
140 |
+ ) { |
|
141 |
+ if (!store.state.isHandlingSessionError) { |
|
142 |
+ store.commit('setHandlingSessionError', true); |
|
143 |
+ originalReq._retry = true; |
|
144 |
+ |
|
145 |
+ try { |
|
146 |
+ await refreshToken(); |
|
147 |
+ originalReq.headers.Authorization = store.state.authorization; |
|
148 |
+ |
|
149 |
+ if ( |
|
150 |
+ originalReq.headers['Content-Type'] && |
|
151 |
+ originalReq.headers['Content-Type'].includes('multipart/form-data') |
|
152 |
+ ) { |
|
153 |
+ return axios({ |
|
154 |
+ ...originalReq, |
|
155 |
+ headers: { |
|
156 |
+ ...originalReq.headers, |
|
157 |
+ Authorization: store.state.authorization, |
|
158 |
+ }, |
|
159 |
+ transformRequest: [(data) => data], |
|
160 |
+ }); |
|
161 |
+ } |
|
162 |
+ } catch (refreshError) { |
|
163 |
+ const redirect = window.location.pathname + window.location.search; |
|
164 |
+ sessionStorage.setItem('redirect', redirect); |
|
165 |
+ alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
166 |
+ store.commit('setStoreReset'); |
|
167 |
+ window.location = '/login.page'; |
|
168 |
+ return Promise.reject(refreshError); |
|
169 |
+ } |
|
170 |
+ } |
|
171 |
+ return Promise.reject(error); |
|
172 |
+ } |
|
173 |
+ |
|
174 |
+ if ( |
|
175 |
+ error.response && |
|
176 |
+ (error.response.status === 400 || error.response.status === 500) |
|
177 |
+ ) { |
|
178 |
+ if (!store.state.isHandlingSessionError) { |
|
179 |
+ store.commit('setHandlingSessionError', true); |
|
180 |
+ const redirect = window.location.pathname + window.location.search; |
|
181 |
+ sessionStorage.setItem('redirect', redirect); |
|
182 |
+ alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
183 |
+ store.commit('setStoreReset'); |
|
184 |
+ window.location = '/login.page'; |
|
185 |
+ } |
|
186 |
+ return Promise.reject(error); |
|
187 |
+ } |
|
188 |
+ |
|
125 | 189 |
return Promise.reject(error); |
126 | 190 |
} |
127 |
- |
|
128 |
- // 401 에러 처리 (토큰 만료) |
|
129 |
- if (error.response && |
|
130 |
- error.response.status === 401 && |
|
131 |
- error.response.data.message === '로그인 시간이 만료되었습니다.' && |
|
132 |
- !originalReq._retry) { |
|
133 |
- |
|
134 |
- originalReq._retry = true; // 재시도 플래그 설정 |
|
135 |
- |
|
136 |
- try { |
|
137 |
- // 토큰 재발급 |
|
138 |
- await refreshToken(); |
|
139 |
- |
|
140 |
- // 새 토큰으로 헤더 업데이트 |
|
141 |
- originalReq.headers.Authorization = store.state.authorization; |
|
142 |
- |
|
143 |
- // multipart/form-data 요청 처리를 위한 특별 처리 |
|
144 |
- if (originalReq.headers['Content-Type'] && |
|
145 |
- originalReq.headers['Content-Type'].includes('multipart/form-data')) { |
|
146 |
- |
|
147 |
- // 완전히 새로운 요청 생성 |
|
148 |
- return axios({ |
|
149 |
- ...originalReq, |
|
150 |
- headers: { |
|
151 |
- ...originalReq.headers, |
|
152 |
- Authorization: store.state.authorization |
|
153 |
- }, |
|
154 |
- // FormData가 변경되지 않도록 원본 데이터 유지 |
|
155 |
- transformRequest: [(data) => data] |
|
156 |
- }); |
|
157 |
- } |
|
158 |
- |
|
159 |
- // 일반 요청은 기존 client 사용하여 재시도 |
|
160 |
- return client(originalReq); |
|
161 |
- } catch (refreshError) { |
|
162 |
- const redirect = window.location.pathname + window.location.search; |
|
163 |
- sessionStorage.setItem("redirect", redirect); |
|
164 |
- alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
165 |
- store.commit("setStoreReset"); |
|
166 |
- window.location = '/login.page'; // 로그인 페이지로 리다이렉트 |
|
167 |
- return Promise.reject(refreshError); |
|
168 |
- } |
|
169 |
- } |
|
170 |
- |
|
171 |
- return Promise.reject(error); |
|
172 |
- } |
|
173 |
- ); |
|
174 |
- |
|
191 |
+ ); |
|
192 |
+ } |
|
175 | 193 |
return client; |
176 | 194 |
}; |
177 | 195 |
|
--- client/views/App.vue
+++ client/views/App.vue
... | ... | @@ -69,7 +69,6 @@ |
69 | 69 |
this.$router.push('/login.page'); |
70 | 70 |
} |
71 | 71 |
} catch (error) { |
72 |
- alert('세션이 종료되었습니다.\n로그인을 새로 해주세요.'); |
|
73 | 72 |
this.store.commit("setStoreReset"); |
74 | 73 |
this.$router.push('/login.page'); // 로그인 페이지로 리다이렉트 |
75 | 74 |
} |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -136,10 +136,14 @@ |
136 | 136 |
|
137 | 137 |
// 로그인 여부 |
138 | 138 |
const isLoggedIn = !!store.state.authorization && !!store.state.userId; |
139 |
+ const isHandlingSession = store.state.isHandlingSessionError; |
|
139 | 140 |
// 로그인이 필요한데 로그인이 안되어 있는 경우 |
140 |
- if (requiresAuth && !isLoggedIn) { |
|
141 |
+ if (requiresAuth && !isLoggedIn && !isHandlingSession) { // 요기! 플래그 조건 추가 |
|
141 | 142 |
alert('로그인이 필요합니다.'); |
142 | 143 |
return next('/Login.page'); |
144 |
+ } else if (requiresAuth && !isLoggedIn && isHandlingSession) { |
|
145 |
+ // 세션 에러 처리 중이면 그냥 로그인 페이지로 이동 |
|
146 |
+ return next('/Login.page'); // 알림 없이 바로 리다이렉트 |
|
143 | 147 |
} |
144 | 148 |
|
145 | 149 |
// 이미 로그인했는데 로그인 페이지로 가려는 경우 |
--- client/views/pages/AppStore.js
+++ client/views/pages/AppStore.js
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 |
loginId: null, // 로그인 ID 추가 |
11 | 11 |
userNm: null, // 사용자 이름 추가 |
12 | 12 |
roles: [], // 사용자 권한 |
13 |
+ isHandlingSessionError: false, |
|
13 | 14 |
}, |
14 | 15 |
getters: { |
15 | 16 |
getAuthorization(state) { |
... | ... | @@ -38,12 +39,16 @@ |
38 | 39 |
setRoles(state, newValue) { |
39 | 40 |
state.roles = newValue; // 사용자 권한 설정 |
40 | 41 |
}, |
42 |
+ setHandlingSessionError(state, newValue) { |
|
43 |
+ state.isHandlingSessionError = newValue; // 요기! 플래그 변경 뮤테이션 |
|
44 |
+ }, |
|
41 | 45 |
setStoreReset(state) { |
42 | 46 |
state.authorization = null; |
43 | 47 |
state.userNm = null; |
44 | 48 |
state.userId = null; |
45 | 49 |
state.roles = []; |
46 | 50 |
state.loginId = null; |
51 |
+ state.isHandlingSessionError = false; |
|
47 | 52 |
}, |
48 | 53 |
}, |
49 | 54 |
actions: { |
--- client/views/pages/bbsDcryPhoto/PicHistoryDetail.vue
+++ client/views/pages/bbsDcryPhoto/PicHistoryDetail.vue
... | ... | @@ -155,10 +155,14 @@ |
155 | 155 |
|
156 | 156 |
this.isRegister = this.$registerChk(this.findResult.register); |
157 | 157 |
} catch (error) { |
158 |
+ if (this.$store.state.isHandlingSessionError) { |
|
159 |
+ console.log("세션 에러 처리 중이라 조회 오류 알림 무시."); |
|
160 |
+ return; // 세션 에러 처리 중이면 여기서 멈춤 |
|
161 |
+ } |
|
158 | 162 |
alert('조회중 오류가 발생했습니다.'); |
159 | 163 |
this.fnMoveTo('list'); |
160 | 164 |
|
161 |
- if (error.response) { |
|
165 |
+ if (error.response && error.response.data && error.response.data.message) { |
|
162 | 166 |
alert(error.response.data.message); |
163 | 167 |
} |
164 | 168 |
console.error(error.message); |
--- client/views/pages/bbsDcryVideo/VideoHistoryDetail.vue
+++ client/views/pages/bbsDcryVideo/VideoHistoryDetail.vue
... | ... | @@ -124,10 +124,14 @@ |
124 | 124 |
} |
125 | 125 |
this.isRegister = this.$registerChk(this.findResult.register); |
126 | 126 |
} catch (error) { |
127 |
+ if (this.$store.state.isHandlingSessionError) { |
|
128 |
+ console.log("세션 에러 처리 중이라 조회 오류 알림 무시."); |
|
129 |
+ return; // 세션 에러 처리 중이면 여기서 멈춤 |
|
130 |
+ } |
|
127 | 131 |
alert('조회중 오류가 발생했습니다.'); |
128 | 132 |
this.fnMoveTo('list'); |
129 | 133 |
|
130 |
- if (error.response) { |
|
134 |
+ if (error.response && error.response.data && error.response.data.message) { |
|
131 | 135 |
alert(error.response.data.message); |
132 | 136 |
} |
133 | 137 |
console.error(error.message); |
--- client/views/pages/bbsMediaVido/MediaVideoDetail.vue
+++ client/views/pages/bbsMediaVido/MediaVideoDetail.vue
... | ... | @@ -116,10 +116,14 @@ |
116 | 116 |
this.findResult = response.data.data.mediaVido; |
117 | 117 |
this.isRegister = this.$registerChk(this.findResult.register); |
118 | 118 |
} catch (error) { |
119 |
+ if (this.$store.state.isHandlingSessionError) { |
|
120 |
+ console.log("세션 에러 처리 중이라 조회 오류 알림 무시."); |
|
121 |
+ return; // 세션 에러 처리 중이면 여기서 멈춤 |
|
122 |
+ } |
|
119 | 123 |
alert('조회중 오류가 발생했습니다.'); |
120 | 124 |
this.fnMoveTo('list'); |
121 | 125 |
|
122 |
- if (error.response) { |
|
126 |
+ if (error.response && error.response.data && error.response.data.message) { |
|
123 | 127 |
alert(error.response.data.message); |
124 | 128 |
} |
125 | 129 |
console.error(error.message); |
--- client/views/pages/bbsNesDta/NewsReleaseDetail.vue
+++ client/views/pages/bbsNesDta/NewsReleaseDetail.vue
... | ... | @@ -120,9 +120,13 @@ |
120 | 120 |
this.findResult = response.data.data.nesDta; |
121 | 121 |
this.isRegister = this.$registerChk(this.findResult.register); |
122 | 122 |
} catch (error) { |
123 |
+ if (this.$store.state.isHandlingSessionError) { |
|
124 |
+ console.log("세션 에러 처리 중이라 조회 오류 알림 무시."); |
|
125 |
+ return; // 세션 에러 처리 중이면 여기서 멈춤 |
|
126 |
+ } |
|
123 | 127 |
alert('조회중 오류가 발생했습니다.'); |
124 | 128 |
|
125 |
- if (error.response) { |
|
129 |
+ if (error.response && error.response.data && error.response.data.message) { |
|
126 | 130 |
alert(error.response.data.message); |
127 | 131 |
} |
128 | 132 |
console.error(error.message); |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?