diff --git a/package-lock.json b/package-lock.json index 303d2f4..95a9eb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@sentry/react": "^10.33.0", "@supabase/supabase-js": "^2.76.1", "@tailwindcss/vite": "^4.1.14", "@tanstack/react-query": "^5.90.9", @@ -2375,6 +2376,97 @@ "win32" ] }, + "node_modules/@sentry-internal/browser-utils": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.33.0.tgz", + "integrity": "sha512-nDJFHAfiFifBfJB0OF6DV6BIsIV5uah4lDsV4UBAgPBf+YAHclO10y1gi2U/JMh58c+s4lXi9p+PI1TFXZ0c6w==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/feedback": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.33.0.tgz", + "integrity": "sha512-sN/VLWtEf0BeV6w6wldIpTxUQxNVc9o9tjLRQa8je1ZV2FCgXA124Iff/zsowsz82dLqtg7qp6GA5zYXVq+JMA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.33.0.tgz", + "integrity": "sha512-UOU9PYxuXnPop3HoQ3l4Q7SZUXJC3Vmfm0Adgad8U03UcrThWIHYc5CxECSrVzfDFNOT7w9o7HQgRAgWxBPMXg==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.33.0", + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.33.0.tgz", + "integrity": "sha512-MTmP6uoAVzw4CCPeqCgCLsRSiOfGLxgyMFjGTCW3E7t62MJ9S0H5sLsQ34sHxXUa1gFU9UNAjEvRRpZ0JvWrPw==", + "license": "MIT", + "dependencies": { + "@sentry-internal/replay": "10.33.0", + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/browser": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.33.0.tgz", + "integrity": "sha512-iWiPjik9zetM84jKfk01UveW1J0+X7w8XmJ8+IrhTyNDBVUWCRJWD8FrksiN1dRSg5mFWgfMRzKMz27hAScRwg==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.33.0", + "@sentry-internal/feedback": "10.33.0", + "@sentry-internal/replay": "10.33.0", + "@sentry-internal/replay-canvas": "10.33.0", + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/core": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.33.0.tgz", + "integrity": "sha512-ehH1VSUclIHZKEZVdv+klofsFIh8FFzqA6AAV23RtLepptzA8wqQzUGraEuSN25sYcNmYJ0jti5U0Ys+WZv5Dw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/react": { + "version": "10.33.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.33.0.tgz", + "integrity": "sha512-iMdC2Iw54ibAccatJ5TjoLlIy3VotFteied7JFvOudgj1/2eBBeWthRobZ5p6/nAOpj4p9vJk0DeLrc012sd2g==", + "license": "MIT", + "dependencies": { + "@sentry/browser": "10.33.0", + "@sentry/core": "10.33.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.14.0 || 17.x || 18.x || 19.x" + } + }, "node_modules/@shikijs/types": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", diff --git a/package.json b/package.json index 1f39e5b..8e34e01 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@sentry/react": "^10.33.0", "@supabase/supabase-js": "^2.76.1", "@tailwindcss/vite": "^4.1.14", "@tanstack/react-query": "^5.90.9", diff --git a/src/lib/sentry.ts b/src/lib/sentry.ts new file mode 100644 index 0000000..c606de6 --- /dev/null +++ b/src/lib/sentry.ts @@ -0,0 +1,27 @@ +import * as Sentry from "@sentry/react"; + +export function initializeSentry() { + const SENTRY_DSN = import.meta.env.VITE_SENTRY_DSN; + + if (SENTRY_DSN) { + Sentry.init({ + dsn: SENTRY_DSN, + // Setting this option to true will send default PII data to Sentry. + // For example, automatic IP address collection on events + sendDefaultPii: true, + integrations: [ + Sentry.browserTracingIntegration(), + Sentry.replayIntegration(), + ], + // Tracing + tracesSampleRate: 1.0, // Capture 100% of the transactions + // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled + tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/], + // Session Replay + replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. + replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur., + // Enable logs to be sent to Sentry + enableLogs: true, + }); + } +} diff --git a/src/main.tsx b/src/main.tsx index 827b85e..b303245 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,29 +16,57 @@ import FindTeammates from "./pages/find-teammates/index.tsx"; import { QueryClientProvider } from "@tanstack/react-query"; import { queryClient } from "@/lib/queryClient"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { initializeSentry } from "@/lib/sentry"; +import * as Sentry from "@sentry/react"; import "./index.css"; -createRoot(document.getElementById("root")!).render( - - - - - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - - , +initializeSentry(); + +const SentryErrorBoundary = Sentry.withErrorBoundary( + () => ( + + + + + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + + + + ), + { + fallback: ({ error, resetError }) => ( +
+
+

문제가 발생했습니다

+

잠시 후 다시 시도해주세요.

+ +
+
+ ), + } ); + +const container = document.getElementById("root"); +if (container) { + const root = createRoot(container); + root.render(); +}