diff --git a/.github/workflows/web-ci.yml b/.github/workflows/web-ci.yml index 065bf301..732ecee5 100644 --- a/.github/workflows/web-ci.yml +++ b/.github/workflows/web-ci.yml @@ -29,8 +29,10 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - cache: 'pnpm' - cache-dependency-path: './src/web' + # cache pnpm에 대한 이슈로 인해 제거 + # https://github.com/actions/setup-node/issues/801 + # cache: 'pnpm' + # cache-dependency-path: './src/web' - name: Cache dependencies id: cache @@ -54,9 +56,6 @@ jobs: - name: check lint run: pnpm lint - - name: check build - run: pnpm build - web-check-type: name: check type runs-on: ubuntu-latest @@ -79,8 +78,8 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - cache: 'pnpm' - cache-dependency-path: './src/web' + # cache: 'pnpm' + # cache-dependency-path: './src/web' - name: Cache dependencies id: cache @@ -120,8 +119,8 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - cache: 'pnpm' - cache-dependency-path: './src/web' + # cache: 'pnpm' + # cache-dependency-path: './src/web' - name: Cache dependencies id: cache @@ -161,8 +160,8 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - cache: 'pnpm' - cache-dependency-path: './src/web' + # cache: 'pnpm' + # cache-dependency-path: './src/web' - name: Cache dependencies id: cache diff --git a/src/web/package.json b/src/web/package.json index cec1e74c..39931554 100644 --- a/src/web/package.json +++ b/src/web/package.json @@ -27,6 +27,7 @@ "lodash.throttle": "^4.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.12", "react-lottie": "^1.2.4", "react-toastify": "^10.0.0", "tailwind-merge": "^2.2.0" diff --git a/src/web/pnpm-lock.yaml b/src/web/pnpm-lock.yaml index 222d434b..25b50e9f 100644 --- a/src/web/pnpm-lock.yaml +++ b/src/web/pnpm-lock.yaml @@ -29,6 +29,9 @@ dependencies: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-error-boundary: + specifier: ^4.0.12 + version: 4.0.12(react@18.2.0) react-lottie: specifier: ^1.2.4 version: 1.2.4(react@18.2.0) @@ -5059,6 +5062,15 @@ packages: react: 18.2.0 scheduler: 0.23.0 + /react-error-boundary@4.0.12(react@18.2.0): + resolution: {integrity: sha512-kJdxdEYlb7CPC1A0SeUY38cHpjuu6UkvzKiAmqmOFL21VRfMhOcWxTCBgLVCO0VEMh9JhFNcVaXlV4/BTpiwOA==} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.23.7 + react: 18.2.0 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true diff --git a/src/web/src/components/AsyncBoundary.tsx b/src/web/src/components/AsyncBoundary.tsx new file mode 100644 index 00000000..e47bb3a6 --- /dev/null +++ b/src/web/src/components/AsyncBoundary.tsx @@ -0,0 +1,34 @@ +import { type ComponentProps, Suspense } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; +import { useQueryErrorResetBoundary } from '@tanstack/react-query'; + +import { ErrorFallbackPage } from '@/pages'; + +type ErrorBoundaryProps = ComponentProps; + +interface AsyncBoundaryProps + extends Omit { + pendingFallback?: ComponentProps['fallback']; + rejectedFallback?: ErrorBoundaryProps['fallbackRender']; +} + +function AsyncBoundary({ + pendingFallback, + rejectedFallback, + children, +}: React.PropsWithChildren) { + const { reset } = useQueryErrorResetBoundary(); + + return ( + + Loading...}> + {children} + + + ); +} + +export default AsyncBoundary; diff --git a/src/web/src/components/index.ts b/src/web/src/components/index.ts index 370588f8..9f99a975 100644 --- a/src/web/src/components/index.ts +++ b/src/web/src/components/index.ts @@ -1,5 +1,6 @@ export * from './common'; export { default as AccessibleIconButton } from './AccessibleIconButton'; +export { default as AsyncBoundary } from './AsyncBoundary'; export { default as BottomNavigation } from './BottomNavigation'; export { default as Header } from './Header'; export { default as NotSupportText } from './NotSupportText'; diff --git a/src/web/src/pages/ErrorFallbackPage.tsx b/src/web/src/pages/ErrorFallbackPage.tsx new file mode 100644 index 00000000..7ed2601f --- /dev/null +++ b/src/web/src/pages/ErrorFallbackPage.tsx @@ -0,0 +1,38 @@ +import type { FallbackProps } from 'react-error-boundary'; + +import { AsyncBoundary, Button, Typography } from '@/components'; + +function ErrorFallbackPage({ error }: FallbackProps) { + return ( + +
+
+ + 😱 + +
+
+ + 잘못된 접근입니다. + + + 메시지:{' '} + {error instanceof Error ? error.message : '잘못된 페이지입니다.'} + +
+ +
+
+ ); +} + +export default ErrorFallbackPage; diff --git a/src/web/src/pages/ErrorPage.tsx b/src/web/src/pages/ErrorPage.tsx new file mode 100644 index 00000000..0621992a --- /dev/null +++ b/src/web/src/pages/ErrorPage.tsx @@ -0,0 +1,5 @@ +function ErrorPage(): JSX.Element { + throw new Error('무엇인가 잘못되었습니다.'); +} + +export default ErrorPage; diff --git a/src/web/src/pages/index.ts b/src/web/src/pages/index.ts index fd9c148d..a3b1f549 100644 --- a/src/web/src/pages/index.ts +++ b/src/web/src/pages/index.ts @@ -1,5 +1,7 @@ export { default as ChangePasswordPage } from './ChangePasswordPage'; export { default as ChatPage } from './ChatPage'; +export { default as ErrorPage } from './ErrorPage'; +export { default as ErrorFallbackPage } from './ErrorFallbackPage'; export { default as HomePage } from './HomePage'; export { default as LoginPage } from './LoginPage'; export { default as MembershipEntryPage } from './MembershipEntryPage'; diff --git a/src/web/src/routes/index.tsx b/src/web/src/routes/index.tsx index 1fd49b4b..0388077d 100644 --- a/src/web/src/routes/index.tsx +++ b/src/web/src/routes/index.tsx @@ -1,8 +1,15 @@ -import { Outlet, Router, Route, RootRoute } from '@tanstack/react-router'; +import { + Outlet, + Router, + Route, + RootRoute, + NotFoundRoute, +} from '@tanstack/react-router'; import { ChangePasswordPage, ChatPage, + ErrorPage, HomePage, LoginPage, MembershipEntryPage, @@ -11,9 +18,14 @@ import { JoinPage, PostEditPage, } from '@/pages'; +import { AsyncBoundary } from '@/components'; export const rootRoute = new RootRoute({ - component: () => , + component: () => ( + Loading...}> + + + ), }); const homeRoute = new Route({ @@ -72,6 +84,15 @@ export const editPostRoute = new Route({ }), }); +const notFoundRoute = new NotFoundRoute({ + getParentRoute: () => rootRoute, + component: () => ( + + + + ), +}); + const routeTree = rootRoute.addChildren([ homeRoute, loginRoute, @@ -84,4 +105,4 @@ const routeTree = rootRoute.addChildren([ postRoute.addChildren([editPostRoute]), ]); -export const router = new Router({ routeTree }); +export const router = new Router({ routeTree, notFoundRoute });