From 5687db9f25cffef4dbe620987d4387ffbf3086cc Mon Sep 17 00:00:00 2001 From: sohot8653 Date: Fri, 26 Sep 2025 09:02:47 +0900 Subject: [PATCH] =?UTF-8?q?[=EC=9D=B8=EC=A6=9D=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0]=20API=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=EC=8B=9C=20=EC=9D=B8=EC=A6=9D=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EB=B0=9C=EC=83=9D=20=EC=8B=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=B8=EC=A6=9D=20=EC=98=A4=EB=A5=98=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80.=20auth.global.ts?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B3=B5=EA=B0=9C=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EC=9D=B8=EC=A6=9D=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8F=AC=ED=95=A8,=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=84=B8=EC=85=98=20=EC=A0=95=EB=A6=AC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composables/useApi.ts | 25 ++++-- middleware/auth.global.ts | 2 +- pages/auth-error.vue | 176 ++++++++++++++++++++++++++++++++++++++ stores/user.ts | 10 +-- 4 files changed, 202 insertions(+), 11 deletions(-) create mode 100644 pages/auth-error.vue diff --git a/composables/useApi.ts b/composables/useApi.ts index a73e076..8c711ac 100644 --- a/composables/useApi.ts +++ b/composables/useApi.ts @@ -75,11 +75,26 @@ export const useApi = async ( ...(headers && { headers }), }; - return ($api as any)(path, apiOpts).catch((error: any) => { + return ($api as any)(path, apiOpts).catch(async (error: any) => { + const status = error.response?.status; + const message = error.response._data.message; + + if ( + status === 401 && + ["JWT_TOKEN_EXPIRED", "INVALID_CLIENT_IP", "JWT_TOKEN_NULL"].includes( + message + ) + ) { + const userStore = useUserStore(); + userStore.user = null; + userStore.isLoggedIn = false; + await navigateTo(`/auth-error?type=${message}`); + throw error; + } + // 사용자에게 알림 표시 if (showAlert) { - const status = error.response?.status; - let message = + let description = status === 404 ? "요청한 리소스를 찾을 수 없습니다." : status === 500 @@ -88,10 +103,10 @@ export const useApi = async ( // 서버에서 온 에러 메시지가 있으면 우선 사용 if (error.response?._data?.description) { - message = error.response._data.description; + description = error.response._data.description; } - alert(message); + alert(description); } // 에러 처리 방식에 따라 반환 diff --git a/middleware/auth.global.ts b/middleware/auth.global.ts index 5691463..9897b60 100644 --- a/middleware/auth.global.ts +++ b/middleware/auth.global.ts @@ -4,7 +4,7 @@ export default defineNuxtRouteMiddleware(async (to, _from) => { const { hasPagePermission } = usePermission(); // 공개 라우트 목록 (로그인 없이 접근 가능) - const publicRoutes = ["/login", "/register"]; + const publicRoutes = ["/login", "/register", "/auth-error"]; // 공개 라우트인지 확인 const isPublicRoute = publicRoutes.some(route => to.path === route); diff --git a/pages/auth-error.vue b/pages/auth-error.vue new file mode 100644 index 0000000..30ab3f0 --- /dev/null +++ b/pages/auth-error.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/stores/user.ts b/stores/user.ts index 833296d..47f5cc6 100644 --- a/stores/user.ts +++ b/stores/user.ts @@ -54,13 +54,13 @@ export const useUserStore = defineStore( showAlert: false, }); - user.value = null; - isLoggedIn.value = false; + // user.value = null; + // isLoggedIn.value = false; - tabsStore.resetTabs(); - permissionsStore.clearPermissions(); + // tabsStore.resetTabs(); + // permissionsStore.clearPermissions(); - await navigateTo("/login"); + // await navigateTo("/login"); }; return {