From c97241bf3e40e9f1e90125b670d5012e416cf263 Mon Sep 17 00:00:00 2001 From: juhyojeong Date: Sun, 11 Aug 2024 21:33:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20error=20boundary=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/yarn.lock | 21 +--- client/package.json | 1 + client/src/components/Layout/index.tsx | 20 ++-- .../CasperCustom/CasperCustomFinish.tsx | 106 ++++++++---------- .../CasperCustom/CasperCustomForm.tsx | 60 +++++----- client/src/pages/ErrorBoundary/index.tsx | 30 ----- client/src/pages/ErrorElement/index.tsx | 19 ++++ client/src/pages/Lottery/index.tsx | 48 ++++---- client/src/router.tsx | 6 +- client/yarn.lock | 27 ++--- 10 files changed, 146 insertions(+), 192 deletions(-) delete mode 100644 client/src/pages/ErrorBoundary/index.tsx create mode 100644 client/src/pages/ErrorElement/index.tsx diff --git a/admin/yarn.lock b/admin/yarn.lock index 950b0081..f2a985b3 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -2708,16 +2708,8 @@ source-map@^0.5.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2789,14 +2781,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== diff --git a/client/package.json b/client/package.json index 6a129616..74001742 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ "react": "^18.3.1", "react-cookie": "^7.2.0", "react-dom": "^18.3.1", + "react-error-boundary": "^4.0.13", "react-router-dom": "^6.25.1", "vite-plugin-svgr": "^4.2.0" }, diff --git a/client/src/components/Layout/index.tsx b/client/src/components/Layout/index.tsx index de08f8eb..878418e7 100644 --- a/client/src/components/Layout/index.tsx +++ b/client/src/components/Layout/index.tsx @@ -1,18 +1,22 @@ import { CookiesProvider } from "react-cookie"; +import { ErrorBoundary } from "react-error-boundary"; import { Outlet } from "react-router-dom"; import { PhoneNumberProvider } from "@/contexts/phoneNumberContext"; import { ScrollHeaderStyleProvider } from "@/contexts/scrollHeaderStyleContext.tsx"; +import ErrorElement from "@/pages/ErrorElement"; import Header from "../Header"; export default function Layout() { return ( - - - -
- - - - + }> + + + +
+ + + + + ); } diff --git a/client/src/features/CasperCustom/CasperCustomFinish.tsx b/client/src/features/CasperCustom/CasperCustomFinish.tsx index 4e0b8914..a3abacd5 100644 --- a/client/src/features/CasperCustom/CasperCustomFinish.tsx +++ b/client/src/features/CasperCustom/CasperCustomFinish.tsx @@ -10,7 +10,6 @@ import { DISSOLVE } from "@/constants/animation"; import useCasperCustomDispatchContext from "@/hooks/useCasperCustomDispatchContext"; import useCasperCustomStateContext from "@/hooks/useCasperCustomStateContext"; import useFetch from "@/hooks/useFetch"; -import ErrorBoundary from "@/pages/ErrorBoundary"; import { CASPER_ACTION } from "@/types/casperCustom"; import { GetApplyCountResponse } from "@/types/lotteryApi"; import { saveDomImage } from "@/utils/saveDomImage"; @@ -30,11 +29,9 @@ export function CasperCustomFinish({ }: CasperCustomFinishProps) { const [cookies] = useCookies([COOKIE_TOKEN_KEY]); - const { - data: applyCountData, - isError: isErrorgetApplyCount, - fetchData: getApplyCount, - } = useFetch(() => LotteryAPI.getApplyCount(cookies[COOKIE_TOKEN_KEY])); + const { data: applyCountData, fetchData: getApplyCount } = useFetch(() => + LotteryAPI.getApplyCount(cookies[COOKIE_TOKEN_KEY]) + ); const dispatch = useCasperCustomDispatchContext(); const { casperName } = useCasperCustomStateContext(); @@ -64,62 +61,57 @@ export function CasperCustomFinish({ }; return ( - - -
-
-
- -
+ +
+
+
+ +
-
- - -
+
+ +
+
-
- {applyCountData && ( -
-

응모한 횟수

- - - -
-

- {applyCountData.appliedCount}회 -

{" "} -

- /{MAX_APPLY}회 -

-
+
+ {applyCountData && ( +
+

응모한 횟수

+ + + +
+

+ {applyCountData.appliedCount}회 +

{" "} +

+ /{MAX_APPLY}회 +

- )} +
+ )} - -
+
- - -

- 다른 사람들의 스마일 로봇 뱃지 보러가기 -

- - - - +
+ + +

+ 다른 사람들의 스마일 로봇 뱃지 보러가기 +

+ + + ); } diff --git a/client/src/features/CasperCustom/CasperCustomForm.tsx b/client/src/features/CasperCustom/CasperCustomForm.tsx index 2c987f39..086b3c8c 100644 --- a/client/src/features/CasperCustom/CasperCustomForm.tsx +++ b/client/src/features/CasperCustom/CasperCustomForm.tsx @@ -10,7 +10,6 @@ import { DISSOLVE } from "@/constants/animation"; import useCasperCustomDispatchContext from "@/hooks/useCasperCustomDispatchContext"; import useCasperCustomStateContext from "@/hooks/useCasperCustomStateContext"; import useFetch from "@/hooks/useFetch"; -import ErrorBoundary from "@/pages/ErrorBoundary"; import { CASPER_ACTION } from "@/types/casperCustom"; import { CasperInformationType, PostCasperResponse } from "@/types/lotteryApi"; import { SCROLL_MOTION } from "../../constants/animation"; @@ -26,7 +25,6 @@ export function CasperCustomForm({ navigateNextStep }: CasperCustomFormProps) { const { data: casper, isSuccess: isSuccessPostCasper, - isError: isErrorPostCasper, fetchData: postCasper, } = useFetch( ({ token, casper }) => LotteryAPI.postCasper(token, casper) @@ -86,37 +84,35 @@ export function CasperCustomForm({ navigateNextStep }: CasperCustomFormProps) { }; return ( - - -
- -
- -
- -
+ +
+ +
+ +
+
+
-
- -
- - +
+ +
+ ); } diff --git a/client/src/pages/ErrorBoundary/index.tsx b/client/src/pages/ErrorBoundary/index.tsx deleted file mode 100644 index 2f42b219..00000000 --- a/client/src/pages/ErrorBoundary/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ReactNode } from "react"; -import CTAButton from "@/components/CTAButton"; - -interface ErrorBoundaryProps { - isError: boolean; - fallbackUrl?: string; - children?: ReactNode; -} - -export default function ErrorBoundary({ - isError, - fallbackUrl = "/", - children, -}: ErrorBoundaryProps) { - if (isError) { - return ( -
- 오류 아이콘 -
-

- 문제가 발생했습니다. 잠시 후 다시 시도해 보세요. -

-
- -
- ); - } - - return children; -} diff --git a/client/src/pages/ErrorElement/index.tsx b/client/src/pages/ErrorElement/index.tsx new file mode 100644 index 00000000..dd867ee3 --- /dev/null +++ b/client/src/pages/ErrorElement/index.tsx @@ -0,0 +1,19 @@ +import CTAButton from "@/components/CTAButton"; + +interface ErrorElementProps { + fallbackUrl?: string; +} + +export default function ErrorElement({ fallbackUrl = "/" }: ErrorElementProps) { + return ( +
+ 오류 아이콘 +
+

+ 문제가 발생했습니다. 잠시 후 다시 시도해 보세요. +

+
+ +
+ ); +} diff --git a/client/src/pages/Lottery/index.tsx b/client/src/pages/Lottery/index.tsx index 6402b63d..448b29df 100644 --- a/client/src/pages/Lottery/index.tsx +++ b/client/src/pages/Lottery/index.tsx @@ -28,7 +28,6 @@ import { PostAuthResponse } from "@/types/authApi"; import { GetLotteryResponse } from "@/types/lotteryApi"; import { PHONE_NUMBER_ACTION } from "@/types/phoneNumber"; import { getMsTime } from "@/utils/getMsTime"; -import ErrorBoundary from "../ErrorBoundary"; export default function Lottery() { useScrollTop(); @@ -43,7 +42,6 @@ export default function Lottery() { const { data: authToken, isSuccess: isSuccessGetAuthToken, - isError: isErrorGetAuthToken, fetchData: getAuthToken, } = useFetch((val: string) => AuthAPI.getAuthToken({ phoneNumber: val }) @@ -94,29 +92,27 @@ export default function Lottery() { }, [lotteryData]); return ( - -
- - - - - - - - - - -
- - {PopupComponent} - {ToastComponent} -
-
+
+ + + + + + + + + + +
+ + {PopupComponent} + {ToastComponent} +
); } diff --git a/client/src/router.tsx b/client/src/router.tsx index 2fd3d201..8ddc81be 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -3,7 +3,7 @@ import { LotteryAPI } from "./apis/lotteryAPI"; import Layout from "./components/Layout"; import CasperCustom from "./pages/CasperCustom"; import CasperShowCase from "./pages/CasperShowCase"; -import ErrorBoundary from "./pages/ErrorBoundary"; +import ErrorElement from "./pages/ErrorElement"; import Lottery from "./pages/Lottery"; import Main from "./pages/Main"; import NotFound from "./pages/NotFound"; @@ -29,7 +29,7 @@ export const router = createBrowserRouter([ index: true, element: , loader: LotteryAPI.getLottery, - errorElement: , + errorElement: , }, { path: "custom", @@ -39,7 +39,7 @@ export const router = createBrowserRouter([ path: "show-case", element: , loader: LotteryAPI.getCasperList, - errorElement: , + errorElement: , }, ], }, diff --git a/client/yarn.lock b/client/yarn.lock index e00f62b4..e76914a0 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5466,6 +5466,13 @@ react-element-to-jsx-string@^15.0.0: is-plain-object "5.0.0" react-is "18.1.0" +react-error-boundary@^4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.13.tgz#80386b7b27b1131c5fbb7368b8c0d983354c7947" + integrity sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ== + dependencies: + "@babel/runtime" "^7.12.5" + react-is@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" @@ -5967,16 +5974,7 @@ storybook@^8.2.5: tiny-invariant "^1.3.1" ts-dedent "^2.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6055,14 +6053,7 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From b169f3d019d95d1701e41355b8092c22bc06d93f Mon Sep 17 00:00:00 2001 From: juhyojeong Date: Sun, 11 Aug 2024 21:37:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20useFetch=20=EC=8B=9C=20error=20boun?= =?UTF-8?q?dary=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useFetch.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/hooks/useFetch.ts b/client/src/hooks/useFetch.ts index db697c6a..fa04daa7 100644 --- a/client/src/hooks/useFetch.ts +++ b/client/src/hooks/useFetch.ts @@ -1,16 +1,20 @@ import { useState } from "react"; +import { useErrorBoundary } from "react-error-boundary"; export default function useFetch(fetch: (params: P) => Promise) { const [data, setData] = useState(null); const [isSuccess, setIsSuccess] = useState(false); const [isError, setIsError] = useState(false); + const { showBoundary } = useErrorBoundary(); + const fetchData = async (params?: P) => { try { const data = await fetch(params as P); setData(data); setIsSuccess(!!data); } catch (error) { + showBoundary(error); setIsError(true); console.error(error); } From a3dd68d55254e76a6dd08d1831dd4b35fc5ca84d Mon Sep 17 00:00:00 2001 From: juhyojeong Date: Sun, 11 Aug 2024 21:46:27 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=84=A0=EC=96=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/Layout/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Layout/index.tsx b/client/src/components/Layout/index.tsx index 878418e7..5170310f 100644 --- a/client/src/components/Layout/index.tsx +++ b/client/src/components/Layout/index.tsx @@ -8,7 +8,7 @@ import Header from "../Header"; export default function Layout() { return ( - }> + }>