diff --git a/package.json b/package.json
index c5f4747..f1e27cb 100644
--- a/package.json
+++ b/package.json
@@ -24,13 +24,14 @@
"nanoid": "^5.0.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-error-boundary": "^4.0.13",
"react-hook-form": "^7.50.1",
"react-router-dom": "^6.22.1",
"react-toastify": "^10.0.4",
"wowds-icons": "^0.1.0",
"wowds-tokens": "^0.0.9",
- "zustand": "^4.5.0",
- "wowds-ui": "^0.1.9"
+ "wowds-ui": "^0.1.9",
+ "zustand": "^4.5.0"
},
"devDependencies": {
"@sentry/react": "^8.22.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dc189e9..5c34b68 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -38,6 +38,9 @@ dependencies:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
+ react-error-boundary:
+ specifier: ^4.0.13
+ version: 4.0.13(react@18.2.0)
react-hook-form:
specifier: ^7.50.1
version: 7.50.1(react@18.2.0)
@@ -8808,6 +8811,15 @@ packages:
react-is: 18.1.0
dev: true
+ /react-error-boundary@4.0.13(react@18.2.0):
+ resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==}
+ peerDependencies:
+ react: '>=16.13.1'
+ dependencies:
+ '@babel/runtime': 7.23.9
+ react: 18.2.0
+ dev: false
+
/react-hook-form@7.50.1(react@18.2.0):
resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==}
engines: {node: '>=12.22.0'}
diff --git a/public/notfound.png b/public/notfound.png
new file mode 100644
index 0000000..3385fa5
Binary files /dev/null and b/public/notfound.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 06d163e..9a801f7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -67,12 +67,14 @@ function App() {
word-break: keep-all;
`}>
GDSC는 Google Developers에서 제공하는 프로그램을 통해 운영되는 대학교
- 기반의 개발자 커뮤니티 그룹입니다.
+ 기반의 개발자 커뮤니티 그룹이에요.
+
개발자로서 성장하는 데 관심이 있는 학부생이라면 누구나 참여할 수
- 있습니다.
+ 있어요.
+
GDSC의 목표는 전 세계의 대학생 개발자들이 구글 관련 기술 혹은
프로그래밍 실력을 높이고 지역 사회와 협력하여 실제 문제를 해결하도록
- 성장하는 것입니다.
+ 성장하는 것이에요.
@@ -97,10 +99,12 @@ function App() {
word-break: keep-all;
`}>
GDSC Hongik Open Community는 학생 개발자를 위한 홍익대학교의 개발
- 커뮤니티입니다.
-
초심자들이 개발에 관심을 가지고 입문할 수 있도록 기초 학술
- 프로그램을 운영하며, 다양한 이벤트와 컨텐츠를 통해 학회원들과 소통할 수
- 있는 네트워킹 플랫폼을 제공하고 있습니다.
+ 커뮤니티예요.
+
+ “더 나은 환경을, 더 나은 학우에게” 제공하는 것을 목표로, 초심자들이
+ 개발에 관심을 가지고 입문할 수 있도록 기초 학술 프로그램을 운영하며,
+ 다양한 이벤트와 컨텐츠를 통해 학회원들과 소통할 수 있는 네트워킹
+ 플랫폼을 제공하고 있어요.
@@ -114,9 +118,8 @@ function App() {
text-align: center;
word-break: keep-all;
`}>
- GDSC의 목표는 전 세계의 대학생 개발자들이 구글 관련 기술 혹은 프로그래밍
- 실력을 높이고 지역 사회와 협력하여 실제 문제를 해결하도록 성장하는
- 것입니다.
+ 이번 학기 GDSC Hongik은 변화와 발전에 초점을 두어, 현재에 안주하지 않고
+ 변화를 통해 구성원이 함께 발전시켜나갈 수 있는 커뮤니티를 운영하려 해요.
@@ -132,17 +135,14 @@ function App() {
text-align: center;
word-break: keep-all;
`}>
- GDSC의 이러한 비전에 맞추어, GDSC Hongik은 초심자들이 개발에 관심을
- 가지고 입문할 수 있도록 기초 학술 프로그램을 운영하며, 다양한 이벤트와
- 콘텐츠를 통해 학회원들과 소통할 수 있는 네트워킹 플랫폼을 제공하고
- 있습니다.
+ 초심자들을 위한 기초 학술 프로그램부터, 다양한 이벤트와 컨텐츠를 통해
+ 학회원들과 소통할 수 있는 네트워킹 플랫폼을 제공할 예정이에요.
@@ -178,17 +177,23 @@ function App() {
+
diff --git a/src/components/ApiErrorBoundary.tsx b/src/components/ApiErrorBoundary.tsx
index d0efcd9..933efbf 100644
--- a/src/components/ApiErrorBoundary.tsx
+++ b/src/components/ApiErrorBoundary.tsx
@@ -1,17 +1,21 @@
-import { PropsWithChildren } from 'react';
-
+import * as Sentry from '@sentry/react';
import { useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { redirect } from 'react-router-dom';
import { toast } from 'react-toastify';
import RoutePath from '@/routes/routePath';
+import { ReactNode } from 'react';
type ErrorResponseType = {
errorCodeName: string;
errorMessage: string;
};
-export default function ApiErrorBoundary({ children }: PropsWithChildren) {
+export default function ApiErrorBoundary({
+ children
+}: {
+ children: ReactNode;
+}) {
const queryClient = useQueryClient();
queryClient.getQueryCache().config = {
@@ -24,7 +28,10 @@ export default function ApiErrorBoundary({ children }: PropsWithChildren) {
function handleError(axiosError: AxiosError) {
const errorResponse = axiosError.response?.data as ErrorResponseType;
-
+ if (errorResponse) {
+ // eslint-disable-next-line import/namespace
+ Sentry.captureException(errorResponse, {});
+ }
const message = errorResponse.errorMessage;
switch (axiosError.response?.status) {
@@ -38,6 +45,11 @@ export default function ApiErrorBoundary({ children }: PropsWithChildren) {
toast.error(message);
break;
}
+
+ if (errorResponse) {
+ // eslint-disable-next-line import/namespace
+ Sentry.captureException(errorResponse, {});
+ }
}
return <>{children}>;
diff --git a/src/components/common/AsyncBoundary.tsx b/src/components/common/AsyncBoundary.tsx
new file mode 100644
index 0000000..7b0793a
--- /dev/null
+++ b/src/components/common/AsyncBoundary.tsx
@@ -0,0 +1,15 @@
+import { ErrorBoundary } from '@sentry/react';
+import { Suspense, SuspenseProps } from 'react';
+import LoadingSpinner from './LoadingSpinner';
+
+type AsyncBoundaryProps = Omit;
+
+const AsyncBoundary = ({ children, ...rest }: AsyncBoundaryProps) => {
+ return (
+
+ }>{children}
+
+ );
+};
+
+export default AsyncBoundary;
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index 50e7d10..a355654 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -27,6 +27,11 @@ const Footer = () => {
GDSC Hongik 가이드라인{' '}
+
+
+ 와우온보딩 이용약관
+
+
diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx
new file mode 100644
index 0000000..c6c3619
--- /dev/null
+++ b/src/pages/NotFound.tsx
@@ -0,0 +1,73 @@
+import { Flex, Space, Text } from '@/components/common/Wrapper';
+import * as Sentry from '@sentry/react';
+import GlobalSize from '@/constants/globalSize';
+import { media } from '@/styles';
+import styled from '@emotion/styled';
+import { useNavigate } from 'react-router-dom';
+import { color } from 'wowds-tokens';
+import Button from 'wowds-ui/Button';
+
+const NotFoundPage = () => {
+ const navigate = useNavigate();
+ if (process.env.NODE_ENV === 'production') {
+ // eslint-disable-next-line import/namespace
+ Sentry.captureMessage('404 Page Not Found', {
+ extra: {
+ pathname: location.pathname
+ }
+ });
+ }
+ return (
+
+
+
+
+ 오류가 발생했어요.
+ 요청하신 페이지를 찾을 수 없어요.
+
+
+
+
+
+ );
+};
+
+export default NotFoundPage;
+
+const Img = styled.img`
+ object-fit: cover;
+`;
+
+const NotfoundWrapper = styled.div`
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ justify-content: start;
+ align-items: center;
+ min-height: calc(100vh - ${GlobalSize.header});
+ width: ${GlobalSize.width};
+ margin: 0px -16px;
+ padding: 0px 16px;
+ padding-top: 40px;
+ padding-bottom: 28px;
+ background-color: ${color.backgroundAlternative};
+ ${media.mobile} {
+ width: 100vw;
+ }
+`;
+
+const ButtonContainer = styled.div`
+ position: absolute;
+ bottom: 1.75rem;
+ padding: 1rem 0.75rem;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+`;
diff --git a/src/pages/index.ts b/src/pages/index.ts
index bc2a706..2465935 100644
--- a/src/pages/index.ts
+++ b/src/pages/index.ts
@@ -11,3 +11,4 @@ export * from './redirect/StudentVerificationServerRedirect';
export * from './PaymentsCheckout';
export * from './PaymentsFail';
export * from './PaymentsSuccess';
+export * from './NotFound';
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 60fff45..dccc10c 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -4,7 +4,7 @@ import RoutePath from '@/routes/routePath';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import Layout from '@/components/layout/Layout';
import AuthAccessGuard from '@/components/auth/guard/AuthAccessGuard';
-import { Text } from '@/components/common/Wrapper';
+import NotFoundPage from '@/pages/NotFound';
import {
AuthServerRedirectNavigate,
StudentVerificationServerRedirect,
@@ -148,8 +148,10 @@ const router = sentryCreateBrowserRouter([
path: RoutePath.PaymentsSuccess,
element:
},
- // Todo: 404 Not found page
- { path: '*', element: not found page }
+ {
+ path: '*',
+ element:
+ }
]
}
]);
diff --git a/src/utils/sentry.ts b/src/utils/sentry.ts
index 8eea869..c71bf51 100644
--- a/src/utils/sentry.ts
+++ b/src/utils/sentry.ts
@@ -12,7 +12,6 @@ const setSentry = () => {
function initSentry() {
if (process.env.NODE_ENV === 'development') return;
- console.log(`VERCEL ENV : ${process.env.VERCEL_ENV}`);
Sentry.init({
environment: process.env.VERCEL_ENV,
dsn: SENTRY_DSN_KEY,
diff --git a/vite.config.ts.timestamp-1724312798440-8bfe4df2623d3.mjs b/vite.config.ts.timestamp-1724312798440-8bfe4df2623d3.mjs
new file mode 100644
index 0000000..3c3b8c1
--- /dev/null
+++ b/vite.config.ts.timestamp-1724312798440-8bfe4df2623d3.mjs
@@ -0,0 +1,27 @@
+// vite.config.ts
+import { defineConfig } from "file:///Users/eugene/github/gdsc-client/node_modules/.pnpm/vite@5.1.4_sass@1.71.1/node_modules/vite/dist/node/index.js";
+import react from "file:///Users/eugene/github/gdsc-client/node_modules/.pnpm/@vitejs+plugin-react-swc@3.6.0_vite@5.1.4/node_modules/@vitejs/plugin-react-swc/index.mjs";
+import tsconfigPaths from "file:///Users/eugene/github/gdsc-client/node_modules/.pnpm/vite-tsconfig-paths@4.3.1_typescript@5.3.3_vite@5.1.4/node_modules/vite-tsconfig-paths/dist/index.mjs";
+import { sentryVitePlugin } from "file:///Users/eugene/github/gdsc-client/node_modules/.pnpm/@sentry+vite-plugin@2.21.1/node_modules/@sentry/vite-plugin/dist/esm/index.mjs";
+var vite_config_default = defineConfig({
+ plugins: [
+ react(),
+ tsconfigPaths(),
+ sentryVitePlugin({
+ org: process.env.SENTRY_ORG_NAME,
+ project: process.env.SENTRY_PROJECT_NAME,
+ authToken: process.env.SENTRY_AUTH_TOKEN,
+ sourcemaps: {
+ assets: "./dist/**",
+ filesToDeleteAfterUpload: "**/*.map"
+ }
+ })
+ ],
+ build: {
+ sourcemap: true
+ }
+});
+export {
+ vite_config_default as default
+};
+//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvZXVnZW5lL2dpdGh1Yi9nZHNjLWNsaWVudFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL2V1Z2VuZS9naXRodWIvZ2RzYy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL2V1Z2VuZS9naXRodWIvZ2RzYy1jbGllbnQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdC1zd2MnO1xuaW1wb3J0IHRzY29uZmlnUGF0aHMgZnJvbSAndml0ZS10c2NvbmZpZy1wYXRocyc7XG5cbmltcG9ydCB7IHNlbnRyeVZpdGVQbHVnaW4gfSBmcm9tICdAc2VudHJ5L3ZpdGUtcGx1Z2luJztcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtcbiAgICByZWFjdCgpLFxuICAgIHRzY29uZmlnUGF0aHMoKSxcbiAgICBzZW50cnlWaXRlUGx1Z2luKHtcbiAgICAgIG9yZzogcHJvY2Vzcy5lbnYuU0VOVFJZX09SR19OQU1FLFxuICAgICAgcHJvamVjdDogcHJvY2Vzcy5lbnYuU0VOVFJZX1BST0pFQ1RfTkFNRSxcbiAgICAgIGF1dGhUb2tlbjogcHJvY2Vzcy5lbnYuU0VOVFJZX0FVVEhfVE9LRU4sXG4gICAgICBzb3VyY2VtYXBzOiB7XG4gICAgICAgIGFzc2V0czogJy4vZGlzdC8qKicsXG4gICAgICAgIGZpbGVzVG9EZWxldGVBZnRlclVwbG9hZDogJyoqLyoubWFwJ1xuICAgICAgfVxuICAgIH0pXG4gIF0sXG4gIGJ1aWxkOiB7XG4gICAgc291cmNlbWFwOiB0cnVlXG4gIH1cbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFrUixTQUFTLG9CQUFvQjtBQUMvUyxPQUFPLFdBQVc7QUFDbEIsT0FBTyxtQkFBbUI7QUFFMUIsU0FBUyx3QkFBd0I7QUFHakMsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUztBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04sY0FBYztBQUFBLElBQ2QsaUJBQWlCO0FBQUEsTUFDZixLQUFLLFFBQVEsSUFBSTtBQUFBLE1BQ2pCLFNBQVMsUUFBUSxJQUFJO0FBQUEsTUFDckIsV0FBVyxRQUFRLElBQUk7QUFBQSxNQUN2QixZQUFZO0FBQUEsUUFDVixRQUFRO0FBQUEsUUFDUiwwQkFBMEI7QUFBQSxNQUM1QjtBQUFBLElBQ0YsQ0FBQztBQUFBLEVBQ0g7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLFdBQVc7QUFBQSxFQUNiO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K