From 35f40b6ad2e185ac8a640a8d33cb8393555edd6a Mon Sep 17 00:00:00 2001 From: GuDoYoon Date: Tue, 7 Jan 2025 14:03:42 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feature(#103):=20Fallback=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 4 +-- src/common/layout/Loader.tsx | 1 - src/features/error/constants.ts | 7 +++++ src/features/error/service/errorUtils.ts | 17 +++++++++--- src/features/error/ui/Fallback.tsx | 33 ++++++++++++++++++------ src/features/error/ui/NotFound.tsx | 11 ++++++++ src/features/quiz/queries.ts | 2 +- src/features/user/ui/TotalResults.tsx | 2 +- src/route/Router.tsx | 2 ++ 9 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/features/error/constants.ts create mode 100644 src/features/error/ui/NotFound.tsx diff --git a/src/App.tsx b/src/App.tsx index f66c1cc..06f9fa1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,11 +12,11 @@ import { Toaster } from 'react-hot-toast'; import Loader from '@common/layout/Loader'; import QueryErrorBoundary from '@features/error/ui/QueryErrorBoundary'; import { Suspense } from 'react'; -import { onError } from '@features/error/service/errorUtils'; +import { handleError } from '@features/error/service/errorUtils'; const queryClient = new QueryClient({ queryCache: new QueryCache({ - onError, + onError: handleError, }), defaultOptions: { mutations: { networkMode: 'always' }, diff --git a/src/common/layout/Loader.tsx b/src/common/layout/Loader.tsx index 02ee7d2..544caf8 100644 --- a/src/common/layout/Loader.tsx +++ b/src/common/layout/Loader.tsx @@ -4,7 +4,6 @@ export default function Loader() { return ( - Loading... ); } diff --git a/src/features/error/constants.ts b/src/features/error/constants.ts new file mode 100644 index 0000000..5449692 --- /dev/null +++ b/src/features/error/constants.ts @@ -0,0 +1,7 @@ +export const HTTP_STATUS_MESSAGE = { + 400: '잘못된 요청입니다.', + 401: '엑세스 권한이 없습니다.', + 403: '리소스에 엑세스할 수 없습니다.', + 404: '존재하지 않는 페이지입니다.', + default: '서버 에러입니다', +} as const; diff --git a/src/features/error/service/errorUtils.ts b/src/features/error/service/errorUtils.ts index 885cf80..f28923f 100644 --- a/src/features/error/service/errorUtils.ts +++ b/src/features/error/service/errorUtils.ts @@ -1,12 +1,23 @@ +import { HTTP_STATUS_MESSAGE } from '@features/error/constants'; import { DefaultError, Query } from '@tanstack/react-query'; import toast from 'react-hot-toast'; -type OnError = ( +type handleError = ( error: DefaultError, query: Query ) => void; -export const onError: OnError = (error, query) => { + +export const handleError: handleError = (error, query) => { if (query.state.data !== undefined) { - toast.error(`백그라운드 데이터 가져오기 실패: ${error.message}`); + return toast.error(`백그라운드 데이터 가져오기 실패: ${error.message}`); } }; + +type StatusCode = keyof typeof HTTP_STATUS_MESSAGE; + +export const getErrorMessage = (statusCode: number): string => { + // 상태 코드가 객체의 키에 존재하는지 확인 + return ( + HTTP_STATUS_MESSAGE[statusCode as StatusCode] || HTTP_STATUS_MESSAGE.default + ); +}; diff --git a/src/features/error/ui/Fallback.tsx b/src/features/error/ui/Fallback.tsx index 77de3b7..92c4f7a 100644 --- a/src/features/error/ui/Fallback.tsx +++ b/src/features/error/ui/Fallback.tsx @@ -1,13 +1,30 @@ +import { isAxiosError } from 'axios'; import { FallbackWrapper } from './styles'; import { FallbackProps } from 'react-error-boundary'; +import { getErrorMessage } from '@features/error/service/errorUtils'; export default function Fallback({ error, resetErrorBoundary }: FallbackProps) { - return ( - -

{error.toString()}

-

오류가 발생했습니다.

-

재시도 해주세요.

- -
- ); + if (!isAxiosError(error)) { + return <>; + } + //스테이터스 코드가 있을 때때 + if (error.status) { + return ( + +

{getErrorMessage(error.status)}

+

재시도 해주세요.

+ +
+ ); + } + + if (error.code && error.code === 'ERR_NETWORK') { + return ( + +

네트워크 에러가 발생했습니다.

+

재시도 해주세요.

+ +
+ ); + } } diff --git a/src/features/error/ui/NotFound.tsx b/src/features/error/ui/NotFound.tsx new file mode 100644 index 0000000..a790579 --- /dev/null +++ b/src/features/error/ui/NotFound.tsx @@ -0,0 +1,11 @@ +import { Link } from 'react-router-dom'; + +export default function NotFoundPage() { + return ( +
+

404

+

페이지를 찾을 수 없습니다.

+ 홈으로 돌아가기 +
+ ); +} diff --git a/src/features/quiz/queries.ts b/src/features/quiz/queries.ts index b5da4e5..29187cc 100644 --- a/src/features/quiz/queries.ts +++ b/src/features/quiz/queries.ts @@ -1,5 +1,5 @@ import quizzesApis from '@features/quiz/apis'; -import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; const quizKeys = { all: ['quizzes'], diff --git a/src/features/user/ui/TotalResults.tsx b/src/features/user/ui/TotalResults.tsx index a000c26..f475837 100644 --- a/src/features/user/ui/TotalResults.tsx +++ b/src/features/user/ui/TotalResults.tsx @@ -29,7 +29,7 @@ export default function TotalResults({ const { mutate: experienceUpdate, isIdle: isexperienceIdle } = experienceQuery.patch(); const { mutate: updateProgress, isIdle: isProgressIdle } = - partProgressQuery.put(); + partProgressQuery.updatePartProgress(); const navigate = useNavigate(); diff --git a/src/route/Router.tsx b/src/route/Router.tsx index de7d26d..9e9c7cd 100644 --- a/src/route/Router.tsx +++ b/src/route/Router.tsx @@ -5,6 +5,7 @@ import Quest from '@/pages/quest/Quest'; import Ranking from '@/pages/ranking/Ranking'; import Quiz from '@/pages/quiz/Quiz'; import Profile from '@/pages/profile/Profile'; +import NotFoundPage from '@features/error/ui/NotFound'; export default function Router() { return ( @@ -18,6 +19,7 @@ export default function Router() { } /> } /> } /> + } />