From 51b4b395fcf9f551507e0bc2652f9fe5ff1535cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9E=AC=ED=9D=AC?= Date: Tue, 28 Nov 2023 11:38:53 +0900 Subject: [PATCH] =?UTF-8?q?:tada:=20=EC=A0=84=EC=97=AD=EC=A0=81=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :tada: 라우트 별 에러 처리 * :tada: 공통 에러 처리 --- src/app/(root)/(routes)/(home)/error.tsx | 70 ------------------- .../(routes)/cards/[cardId]/modify/error.tsx | 62 ---------------- src/app/(root)/(routes)/cards/new/error.tsx | 31 -------- .../(routes)/chatrooms/[chatRoomId]/error.tsx | 62 ---------------- .../(routes)/chatrooms/[chatRoomId]/page.tsx | 32 ++++++--- src/app/(root)/(routes)/mypage/error.tsx | 31 -------- .../(routes)/suggestions/[myCardId]/page.tsx | 25 +++++-- src/app/(root)/not-found.tsx | 12 ---- src/app/error.tsx | 15 ++++ src/app/global-error.tsx | 18 ----- src/app/not-found.tsx | 6 ++ .../domain/errors/DefaultErrorTemplate.tsx | 47 +++++++++++++ src/components/domain/errors/ErrorGateway.tsx | 23 ++++++ .../domain/errors/ForbiddenErrorTemplate.tsx | 49 +++++++++++++ .../domain/errors/NotFoundErrorTemplate.tsx | 52 ++++++++++++++ .../header/components/NotificationButton.tsx | 2 +- src/config/errorCodes.ts | 8 +++ src/hooks/useValidate.ts | 17 +++-- src/middleware.ts | 21 +++--- 19 files changed, 267 insertions(+), 316 deletions(-) delete mode 100644 src/app/(root)/(routes)/(home)/error.tsx delete mode 100644 src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx delete mode 100644 src/app/(root)/(routes)/cards/new/error.tsx delete mode 100644 src/app/(root)/(routes)/chatrooms/[chatRoomId]/error.tsx delete mode 100644 src/app/(root)/(routes)/mypage/error.tsx delete mode 100644 src/app/(root)/not-found.tsx create mode 100644 src/app/error.tsx delete mode 100644 src/app/global-error.tsx create mode 100644 src/app/not-found.tsx create mode 100644 src/components/domain/errors/DefaultErrorTemplate.tsx create mode 100644 src/components/domain/errors/ErrorGateway.tsx create mode 100644 src/components/domain/errors/ForbiddenErrorTemplate.tsx create mode 100644 src/components/domain/errors/NotFoundErrorTemplate.tsx create mode 100644 src/config/errorCodes.ts diff --git a/src/app/(root)/(routes)/(home)/error.tsx b/src/app/(root)/(routes)/(home)/error.tsx deleted file mode 100644 index bec79a27..00000000 --- a/src/app/(root)/(routes)/(home)/error.tsx +++ /dev/null @@ -1,70 +0,0 @@ -'use client' - -// Error components must be Client Components -import { useEffect } from 'react' -import { useRouter } from 'next/navigation' -import AppPath from '@/config/appPath' -import ErrorMessages from '@/config/errorMessages' -import { useToast } from '@/hooks/useToast' - -export default function ErrorPage({ - error, - reset, -}: { - error: Error & { digest?: string } - reset: () => void -}) { - const router = useRouter() - const { toast } = useToast() - useEffect(() => { - // Log the error to an error reporting service - - if (error.message === ErrorMessages.Forbidden) { - console.log('ForbiddenError') - toast({ - title: 'Forbidden', - description: 'You do not have permission to access this page.', - duration: 2000, - }) - } - if (error.message === ErrorMessages.Unauthorized) { - router.push(AppPath.login()) - toast({ - title: 'Unauthorized', - description: 'Please login to access this page.', - duration: 2000, - }) - } - if (error.message === ErrorMessages.NotFound) { - toast({ - title: 'Not Found', - description: 'Please login to access this page.', - duration: 2000, - }) - } - if (error.message === ErrorMessages.ServerError) { - toast({ - title: '서버 에러', - description: ErrorMessages.ServerError, - duration: 2000, - }) - } - }, [error, router, toast]) - - return ( -
-

Something went wrong!

-

- Error: {error.message} ({error?.name}) -

- -
- ) -} diff --git a/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx b/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx deleted file mode 100644 index afffdf69..00000000 --- a/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client' - -// Error components must be Client Components -import { useEffect } from 'react' -import { useRouter } from 'next/navigation' -import AppPath from '@/config/appPath' -import ErrorMessages from '@/config/errorMessages' -import { useToast } from '@/hooks/useToast' - -export default function Error({ - error, - reset, -}: { - error: Error & { digest?: string } - reset: () => void -}) { - const router = useRouter() - const { toast } = useToast() - useEffect(() => { - console.log(error.digest, error.message, error.name) - if (error.message === ErrorMessages.Forbidden) { - console.log('ForbiddenError') - toast({ - title: 'Forbidden', - description: ErrorMessages.Forbidden, - duration: 2000, - }) - } - if (error.message === ErrorMessages.Unauthorized) { - router.push(AppPath.login()) - toast({ - title: 'Unauthorized', - description: ErrorMessages.Unauthorized, - duration: 2000, - }) - } - if (error.message === ErrorMessages.NotFound) { - toast({ - title: 'Not Found', - description: ErrorMessages.NotFound, - duration: 2000, - }) - } - }, [error, router, toast]) - - return ( -
-

Something went wrong!

-

- Error: {error.message} ({error?.name}) -

- -
- ) -} diff --git a/src/app/(root)/(routes)/cards/new/error.tsx b/src/app/(root)/(routes)/cards/new/error.tsx deleted file mode 100644 index 2cdc24d0..00000000 --- a/src/app/(root)/(routes)/cards/new/error.tsx +++ /dev/null @@ -1,31 +0,0 @@ -'use client' - -// Error components must be Client Components -import { useEffect } from 'react' - -export default function Error({ - error, - reset, -}: { - error: Error & { digest?: string } - reset: () => void -}) { - useEffect(() => { - // Log the error to an error reporting service - console.error(error) - }, [error]) - - return ( -
-

Something went wrong!

- -
- ) -} diff --git a/src/app/(root)/(routes)/chatrooms/[chatRoomId]/error.tsx b/src/app/(root)/(routes)/chatrooms/[chatRoomId]/error.tsx deleted file mode 100644 index afffdf69..00000000 --- a/src/app/(root)/(routes)/chatrooms/[chatRoomId]/error.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client' - -// Error components must be Client Components -import { useEffect } from 'react' -import { useRouter } from 'next/navigation' -import AppPath from '@/config/appPath' -import ErrorMessages from '@/config/errorMessages' -import { useToast } from '@/hooks/useToast' - -export default function Error({ - error, - reset, -}: { - error: Error & { digest?: string } - reset: () => void -}) { - const router = useRouter() - const { toast } = useToast() - useEffect(() => { - console.log(error.digest, error.message, error.name) - if (error.message === ErrorMessages.Forbidden) { - console.log('ForbiddenError') - toast({ - title: 'Forbidden', - description: ErrorMessages.Forbidden, - duration: 2000, - }) - } - if (error.message === ErrorMessages.Unauthorized) { - router.push(AppPath.login()) - toast({ - title: 'Unauthorized', - description: ErrorMessages.Unauthorized, - duration: 2000, - }) - } - if (error.message === ErrorMessages.NotFound) { - toast({ - title: 'Not Found', - description: ErrorMessages.NotFound, - duration: 2000, - }) - } - }, [error, router, toast]) - - return ( -
-

Something went wrong!

-

- Error: {error.message} ({error?.name}) -

- -
- ) -} diff --git a/src/app/(root)/(routes)/chatrooms/[chatRoomId]/page.tsx b/src/app/(root)/(routes)/chatrooms/[chatRoomId]/page.tsx index 4c6c1856..2c765bb8 100644 --- a/src/app/(root)/(routes)/chatrooms/[chatRoomId]/page.tsx +++ b/src/app/(root)/(routes)/chatrooms/[chatRoomId]/page.tsx @@ -1,6 +1,9 @@ import React from 'react' import PageTitle from '@/components/domain/page-title' import ApiEndPoint from '@/config/apiEndPoint' +import errorCodes from '@/config/errorCodes' +import ErrorMessages from '@/config/errorMessages' +import { ForbiddenError } from '@/lib/errors' import apiClient from '@/services/apiClient' import { getServerCookie } from '@/utils/getServerCookie' import ChatRoomTemplate from './components/ChatRoomTemplate' @@ -26,15 +29,24 @@ const getInitialUser = async () => { } const getInitialChatRoom = async (chatRoomId: string) => { - const token = getServerCookie() - const res = await apiClient.get( - ApiEndPoint.getChatRoom(chatRoomId), - {}, - { - Authorization: `${token}`, - }, - ) - return res.data.chatRoomInfo + try { + const token = getServerCookie() + const res = await apiClient.get( + ApiEndPoint.getChatRoom(chatRoomId), + {}, + { + Authorization: `${token}`, + }, + ) + return res.data.chatRoomInfo + } catch (e: any) { + const log = await e.response.json() + if (log.code === errorCodes.forbidden.code) { + throw new ForbiddenError(new Response(ErrorMessages.Forbidden)) + } + + throw new Error(e) + } } const getCompleteRequestInfo = async (completeRequestId: number) => { @@ -74,7 +86,7 @@ const ChatPage = async ({ params }: ChatPageProps) => { return (
-
+
{!isCompleteRequested && ( void -}) { - useEffect(() => { - // Log the error to an error reporting service - console.error(error) - }, [error]) - - return ( -
-

Something went wrong!

- -
- ) -} diff --git a/src/app/(root)/(routes)/suggestions/[myCardId]/page.tsx b/src/app/(root)/(routes)/suggestions/[myCardId]/page.tsx index 6b229fa7..ec689ac8 100644 --- a/src/app/(root)/(routes)/suggestions/[myCardId]/page.tsx +++ b/src/app/(root)/(routes)/suggestions/[myCardId]/page.tsx @@ -1,17 +1,32 @@ import MaxWidthWrapper from '@/components/domain/max-width-wrapper' import PageTitle from '@/components/domain/page-title' +import ApiEndPoint from '@/config/apiEndPoint' +import ErrorMessages from '@/config/errorMessages' +import { ForbiddenError } from '@/lib/errors' +import apiClient from '@/services/apiClient' import { getCardInfo } from '@/services/card/card' import { CardDetail } from '@/types/card' +import { getServerCookie } from '@/utils/getServerCookie' import MyCardDescriptionSection from './components/my-card-description-section' import MySuggestionListContent from './components/my-suggestion-list-content' async function getMyCardInfo(cardId: string) { - try { - const res = await getCardInfo(Number(cardId)) - return res.data.cardInfo - } catch (e) { - console.log(e) + const token = getServerCookie() + + const resCard = await getCardInfo(Number(cardId)) + const resUser = await apiClient.get( + ApiEndPoint.getValidateUser(), + {}, + { + Authorization: `${token}`, + }, + ) + + if (resUser.data.userInfo.userId !== resCard.data.userInfo.userId) { + throw new ForbiddenError(new Response(ErrorMessages.Forbidden)) } + + return resCard.data.cardInfo } const SuggestCheckListPage = async ({ diff --git a/src/app/(root)/not-found.tsx b/src/app/(root)/not-found.tsx deleted file mode 100644 index 30d6214c..00000000 --- a/src/app/(root)/not-found.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import Link from 'next/link' -import AppPath from '@/config/appPath' - -export default function NotFound() { - return ( -
- Not Founded - 홈페이지로 돌아가기 -
- ) -} diff --git a/src/app/error.tsx b/src/app/error.tsx new file mode 100644 index 00000000..87bb9ab9 --- /dev/null +++ b/src/app/error.tsx @@ -0,0 +1,15 @@ +'use client' + +import ErrorGateway from '@/components/domain/errors/ErrorGateway' + +type ErrorPageProps = { + error: Error & { digest?: string } + reset: () => void +} + +export default function GlobalError({ + error, + reset, +}: Readonly) { + return +} diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx deleted file mode 100644 index d3d27b4b..00000000 --- a/src/app/global-error.tsx +++ /dev/null @@ -1,18 +0,0 @@ -'use client' -//TODO: 추가 테스트 후 코드 추가 -export default function GlobalError({ - _error, - reset, -}: { - _error: Error - reset: () => void -}) { - return ( - - -

Something went wrong!

- - - - ) -} diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 00000000..4dbdcdb7 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import NotFoundErrorTemplate from '@/components/domain/errors/NotFoundErrorTemplate' + +export default function NotFound() { + return +} diff --git a/src/components/domain/errors/DefaultErrorTemplate.tsx b/src/components/domain/errors/DefaultErrorTemplate.tsx new file mode 100644 index 00000000..7c87cf63 --- /dev/null +++ b/src/components/domain/errors/DefaultErrorTemplate.tsx @@ -0,0 +1,47 @@ +'use client' + +import React from 'react' +import Image from 'next/image' +import { useRouter } from 'next/navigation' +import Button from '@/components/ui/button' +import AppPath from '@/config/appPath' +import Assets from '@/config/assets' + +type DefaultErrorTemplateProps = { + onClickButton: () => void +} + +const DefaultErrorTemplate = ({ onClickButton }: DefaultErrorTemplateProps) => { + const router = useRouter() + + const onClickHomeButton = () => { + router.push(AppPath.home()) + } + + return ( +
+ no-data + + 알 수 없는 오류가 발생했습니다. + +
+ + +
+
+ ) +} + +export default DefaultErrorTemplate diff --git a/src/components/domain/errors/ErrorGateway.tsx b/src/components/domain/errors/ErrorGateway.tsx new file mode 100644 index 00000000..7fb8f513 --- /dev/null +++ b/src/components/domain/errors/ErrorGateway.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import DefaultErrorTemplate from '@/components/domain/errors/DefaultErrorTemplate' +import ForbiddenErrorTemplate from '@/components/domain/errors/ForbiddenErrorTemplate' +import NotFoundErrorTemplate from '@/components/domain/errors/NotFoundErrorTemplate' +import ErrorMessages from '@/config/errorMessages' + +type ErrorGatewayProps = { + error: Error & { digest?: string } + reset: () => void +} + +const ErrorGateway = ({ error, reset }: ErrorGatewayProps) => { + switch (error.message) { + case ErrorMessages.Forbidden: + return reset()} /> + case ErrorMessages.NotFound: + return reset()} /> + default: + return reset()} /> + } +} + +export default ErrorGateway diff --git a/src/components/domain/errors/ForbiddenErrorTemplate.tsx b/src/components/domain/errors/ForbiddenErrorTemplate.tsx new file mode 100644 index 00000000..c1835c9e --- /dev/null +++ b/src/components/domain/errors/ForbiddenErrorTemplate.tsx @@ -0,0 +1,49 @@ +'use client' + +import React from 'react' +import Image from 'next/image' +import { useRouter } from 'next/navigation' +import Button from '@/components/ui/button' +import AppPath from '@/config/appPath' +import Assets from '@/config/assets' + +type ForbiddenErrorTemplateProps = { + onClickButton: () => void +} + +const ForbiddenErrorTemplate = ({ + onClickButton, +}: ForbiddenErrorTemplateProps) => { + const router = useRouter() + + const onClickHomeButton = () => { + router.push(AppPath.home()) + } + + return ( +
+ no-data + + 해당 컨텐츠에 접근할 권한이 없습니다. + +
+ + +
+
+ ) +} + +export default ForbiddenErrorTemplate diff --git a/src/components/domain/errors/NotFoundErrorTemplate.tsx b/src/components/domain/errors/NotFoundErrorTemplate.tsx new file mode 100644 index 00000000..26d4fb6a --- /dev/null +++ b/src/components/domain/errors/NotFoundErrorTemplate.tsx @@ -0,0 +1,52 @@ +'use client' + +import React from 'react' +import Image from 'next/image' +import { useRouter } from 'next/navigation' +import Button from '@/components/ui/button' +import AppPath from '@/config/appPath' +import Assets from '@/config/assets' + +type NotFoundErrorProps = { + onClickButton?: () => void +} + +const NotFoundErrorTemplate = ({ onClickButton }: NotFoundErrorProps) => { + const router = useRouter() + + const onClickHomeButton = () => { + router.push(AppPath.home()) + } + + return ( +
+ no-data + + 404 Not Found + + + 컨텐츠를 찾을 수 없습니다. + +
+ {onClickButton && ( + + )} + +
+
+ ) +} + +export default NotFoundErrorTemplate diff --git a/src/components/domain/header/components/NotificationButton.tsx b/src/components/domain/header/components/NotificationButton.tsx index e160268b..33ddb100 100644 --- a/src/components/domain/header/components/NotificationButton.tsx +++ b/src/components/domain/header/components/NotificationButton.tsx @@ -30,7 +30,7 @@ const NotificationButton = ({ > diff --git a/src/config/errorCodes.ts b/src/config/errorCodes.ts new file mode 100644 index 00000000..4577f284 --- /dev/null +++ b/src/config/errorCodes.ts @@ -0,0 +1,8 @@ +const errorCodes = { + forbidden: { + code: 'UOO02', + message: '적합한 사용자가 아닙니다.', + }, +} + +export default errorCodes diff --git a/src/hooks/useValidate.ts b/src/hooks/useValidate.ts index 2ae19845..65f34682 100644 --- a/src/hooks/useValidate.ts +++ b/src/hooks/useValidate.ts @@ -4,16 +4,17 @@ import { useEffect, useState } from 'react' import { useQuery } from '@tanstack/react-query' import Cookies from 'js-cookie' import { usePathname, useRouter } from 'next/navigation' -import AppPath from '@/config/appPath' import { Environment } from '@/config/environment' import apiClient from '@/services/apiClient' import { getValidateUser } from '@/services/auth/auth' import type { User } from '@/types/user' +import { useToast } from './useToast' const useValidate = () => { const token = Cookies.get(Environment.tokenName()) const router = useRouter() const pathname = usePathname() + const { toast } = useToast() const [isLoggedIn, setIsLoggedIn] = useState(!!token) const [currentUser, setCurrentUser] = useState(null) @@ -31,10 +32,16 @@ const useValidate = () => { useEffect(() => { console.log(currentUser) if (isError) { - Cookies.remove(Environment.tokenName()) - setIsLoggedIn(() => false) - setCurrentUser(() => null) - router.push(AppPath.login(), { scroll: false }) + // Cookies.remove(Environment.tokenName()) + // setIsLoggedIn(() => false) + // setCurrentUser(() => null) + // router.push(AppPath.login(), { scroll: false }) + // toast({ + // title: '인증 에러', + // description: '인증에 실패하였습니다. 다시 시도하거나 로그인해주세요.', + // variant: 'destructive', + // duration: 3000, + // }) } if (data) { setIsLoggedIn(() => true) diff --git a/src/middleware.ts b/src/middleware.ts index fe1cf74a..e073f50f 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -12,14 +12,6 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(new URL(AppPath.login(), request.url)) } - // if (request.nextUrl.pathname.startsWith('/chatrooms')) { - // const chatRoomId = request.nextUrl.pathname.split('/')[2] - // if (!chatRoomId) return - // console.log(chatRoomId) - - // return NextResponse.rewrite(new URL('/chatrooms', request.url)) - // } - try { const res = await getValidateUser() @@ -45,5 +37,16 @@ export async function middleware(request: NextRequest) { // See "Matching Paths" below to learn more export const config = { - matcher: ['/mypage', '/cards/:path/modify', '/cards/new'], + matcher: [ + '/mypage', + '/cards/:path/modify', + '/cards/new', + '/dibs', + '/cards/my', + '/notifications', + '/suggestions/:path*', + '/history', + '/chatrooms', + '/chatrooms/:path*', + ], }