From f9dde4eb09c8566e1890442a38027d0861b15ea5 Mon Sep 17 00:00:00 2001 From: sohot8653 Date: Wed, 24 Sep 2025 16:25:30 +0900 Subject: [PATCH] =?UTF-8?q?[UI=20=EA=B0=9C=EC=84=A0]=20=EC=84=9C=EB=B8=8C?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EB=B0=8F=20=ED=83=AD=20=EB=B0=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80,=20Ap?= =?UTF-8?q?pHeader=20=EB=B0=8F=20=EA=B8=B0=EB=B3=B8=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95,=20API=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=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 --- components/layout/AppHeader.vue | 14 +- components/layout/SubMenuBar.vue | 66 ++++++++ components/layout/TabBar.vue | 135 +++++++++++++++ composables/useApi.ts | 115 ++++++++----- layouts/default.vue | 159 ++++-------------- pages/about.vue | 276 ------------------------------- pages/index.vue | 9 +- stores/permissions.ts | 2 +- stores/tab.ts | 14 +- stores/user.ts | 38 ++--- 10 files changed, 338 insertions(+), 490 deletions(-) create mode 100644 components/layout/SubMenuBar.vue create mode 100644 components/layout/TabBar.vue delete mode 100644 pages/about.vue diff --git a/components/layout/AppHeader.vue b/components/layout/AppHeader.vue index c0cc4d5..7dac7bc 100644 --- a/components/layout/AppHeader.vue +++ b/components/layout/AppHeader.vue @@ -6,8 +6,8 @@ @@ -72,7 +72,6 @@ + + diff --git a/components/layout/TabBar.vue b/components/layout/TabBar.vue new file mode 100644 index 0000000..33a0544 --- /dev/null +++ b/components/layout/TabBar.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/composables/useApi.ts b/composables/useApi.ts index aa1b1eb..a73e076 100644 --- a/composables/useApi.ts +++ b/composables/useApi.ts @@ -7,17 +7,20 @@ * @returns Promise - API 응답 데이터 * * @example - * // GET 요청 (기본) + * // GET 요청 (기본 - 전역 로딩 자동 적용) * const users = await useApi('/users') * - * // POST 요청 + * // POST 요청 (커스텀 로딩 메시지) * const newUser = await useApi('/users', { * method: 'POST', - * body: { name: 'John', email: 'john@example.com' } + * body: { name: 'John', email: 'john@example.com' }, + * loadingMessage: '사용자를 생성하는 중...' * }) * - * // 에러 시 alert 표시 - * const data = await useApi('/users', { showAlert: true }) + * // 전역 로딩 없이 API 호출 + * const data = await useApi('/users', { + * useGlobalLoading: false + * }) * * // 에러를 직접 처리 * try { @@ -29,7 +32,11 @@ * // FormData 업로드 * const formData = new FormData() * formData.append('file', file) - * await useApi('/upload', { method: 'POST', body: formData }) + * await useApi('/upload', { + * method: 'POST', + * body: formData, + * loadingMessage: '파일을 업로드하는 중...' + * }) */ export const useApi = async ( path: string, @@ -41,50 +48,72 @@ export const useApi = async ( // 에러 처리 옵션 handleError?: boolean; // true: 에러를 null로 반환, false: 에러를 다시 던짐 showAlert?: boolean; // true: 에러 시 alert 표시 + // 로딩 옵션 + loadingMessage?: string; // 로딩 메시지 + useGlobalLoading?: boolean; // 전역 로딩 사용 여부 (기본값: true) } ): Promise => { - const { $api } = useNuxtApp(); + const { withLoading } = useLoading(); - // 기본값 설정 - const { - method = "GET", - body, - headers, - handleError = true, - showAlert = true, - } = options || {}; + // API 호출 로직을 별도 함수로 분리 + const apiCall = async (): Promise => { + const { $api } = useNuxtApp(); - // API 요청 옵션 구성 - const apiOpts = { - method, - ...(body && { body }), - ...(headers && { headers }), - }; + // 기본값 설정 + const { + method = "GET", + body, + headers, + handleError = true, + showAlert = true, + } = options || {}; - return ($api as any)(path, apiOpts).catch((error: any) => { - // 사용자에게 알림 표시 - if (showAlert) { - const status = error.response?.status; - let message = - status === 404 - ? "요청한 리소스를 찾을 수 없습니다." - : status === 500 - ? "서버 오류가 발생했습니다." - : "요청 처리 중 오류가 발생했습니다."; + // API 요청 옵션 구성 + const apiOpts = { + method, + ...(body && { body }), + ...(headers && { headers }), + }; - // 서버에서 온 에러 메시지가 있으면 우선 사용 - if (error.response?._data?.description) { - message = error.response._data.description; + return ($api as any)(path, apiOpts).catch((error: any) => { + // 사용자에게 알림 표시 + if (showAlert) { + const status = error.response?.status; + let message = + status === 404 + ? "요청한 리소스를 찾을 수 없습니다." + : status === 500 + ? "서버 오류가 발생했습니다." + : "요청 처리 중 오류가 발생했습니다."; + + // 서버에서 온 에러 메시지가 있으면 우선 사용 + if (error.response?._data?.description) { + message = error.response._data.description; + } + + alert(message); } - alert(message); - } + // 에러 처리 방식에 따라 반환 + if (handleError) { + return null as T; // 에러를 null로 반환 + } else { + throw error; // 에러를 다시 던짐 + } + }); + }; - // 에러 처리 방식에 따라 반환 - if (handleError) { - return null as T; // 에러를 null로 반환 - } else { - throw error; // 에러를 다시 던짐 - } - }); + // 전역 로딩 사용 여부 확인 (기본값: true) + const shouldUseLoading = options?.useGlobalLoading !== false; + + if (shouldUseLoading) { + // 전역 로딩과 함께 API 호출 + return await withLoading( + apiCall, + options?.loadingMessage || "데이터를 불러오는 중..." + ); + } else { + // 전역 로딩 없이 API 호출 + return await apiCall(); + } }; diff --git a/layouts/default.vue b/layouts/default.vue index 8f79e6d..a438e3c 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,6 +1,28 @@ + + - - diff --git a/pages/about.vue b/pages/about.vue deleted file mode 100644 index 4ce76e5..0000000 --- a/pages/about.vue +++ /dev/null @@ -1,276 +0,0 @@ - - - - - diff --git a/pages/index.vue b/pages/index.vue index 7545232..f9229e1 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -100,12 +100,11 @@