diff --git a/package.json b/package.json index 9e59ad1..2ffd877 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kmong-developers", - "version": "0.1.0", + "version": "1.0.0", "private": true, "scripts": { "dev": "next dev", diff --git a/sentry.client.config.ts b/sentry.client.config.ts index 9153976..c0baa69 100644 --- a/sentry.client.config.ts +++ b/sentry.client.config.ts @@ -3,28 +3,55 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from "@sentry/nextjs"; +import packageInfo from './package.json'; Sentry.init({ - dsn: "https://78244d347b94fbc2be076209148d8ab4@o4506552386715648.ingest.sentry.io/4506552392417280", + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, // Adjust this value in production, or use tracesSampler for greater control tracesSampleRate: 1, // Setting this option to true will print useful information to the console while you're setting up Sentry. - debug: false, - + debug: true, + environment: process.env.NODE_ENV, + release: packageInfo.version, replaysOnErrorSampleRate: 1.0, // This sets the sample rate to be 10%. You may want this to be 100% while // in development and sample at a lower rate in production replaysSessionSampleRate: 0.1, - - // You can remove this option if you're not planning to use the Sentry Session Replay feature: + ignoreErrors: ['밀크티'], + // denyUrls: ['sentry-example-page'], + // allowUrls: ['sentry-example-page'], + autoSessionTracking: true, integrations: [ new Sentry.Replay({ // Additional Replay configuration goes in here, for example: - maskAllText: true, + maskAllText: false, blockAllMedia: true, }), + new Sentry.Integrations.Breadcrumbs({ + console: false, + }), + new Sentry.BrowserTracing({ + shouldCreateSpanForRequest: (url) => { + // `/sentry-example-api`로 요청하는 경우 span을 생성하지 않는다. >> Performance 대시보드에 생성되지 않음 + console.log('url', url); + // console.log('isMatch?', !url.match(/\/sentry-example-api?$/)); + return !url.match(/\/sentry-example-api?$/); + }, + beforeNavigate(context) { + console.log('beforeNavigate', context); + + if(/^\/order\/\d+$/.test(context.name)) { + return { + ...context, + name: 'order/:id', + } + } + + return context; + }, + }), ], }); diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts index 03a5c53..efc59b6 100644 --- a/sentry.edge.config.ts +++ b/sentry.edge.config.ts @@ -6,7 +6,7 @@ import * as Sentry from "@sentry/nextjs"; Sentry.init({ - dsn: "https://78244d347b94fbc2be076209148d8ab4@o4506552386715648.ingest.sentry.io/4506552392417280", + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, // Adjust this value in production, or use tracesSampler for greater control tracesSampleRate: 1, diff --git a/sentry.server.config.ts b/sentry.server.config.ts index 7a55911..86d85e1 100644 --- a/sentry.server.config.ts +++ b/sentry.server.config.ts @@ -5,7 +5,7 @@ import * as Sentry from "@sentry/nextjs"; Sentry.init({ - dsn: "https://78244d347b94fbc2be076209148d8ab4@o4506552386715648.ingest.sentry.io/4506552392417280", + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, // Adjust this value in production, or use tracesSampler for greater control tracesSampleRate: 1, diff --git a/src/app/api/sentry-example-api/route.js b/src/app/api/sentry-example-api/route.js new file mode 100644 index 0000000..f486f3d --- /dev/null +++ b/src/app/api/sentry-example-api/route.js @@ -0,0 +1,9 @@ +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +// A faulty API route to test Sentry's error monitoring +export function GET() { + throw new Error("Sentry Example API Route Error"); + return NextResponse.json({ data: "Testing Sentry Error..." }); +} diff --git a/src/app/cart/page.tsx b/src/app/cart/page.tsx new file mode 100644 index 0000000..d1ae9c5 --- /dev/null +++ b/src/app/cart/page.tsx @@ -0,0 +1,47 @@ +"use client"; + +import styles from "@/app/order/order.module.css"; +import Link from "next/link"; + +function CartPage() { + const handleClickPayment = async () => { + console.log('결제를 시작합니다.'); + + const res = await fetch("/api/sentry-example-api", { method: 'post' }); + if (!res.ok) { + throw new Error("결제 API 실패"); + } + } + + return ( +
+
+ + 홈 + +

장바구니 페이지

+ + 주문 페이지 + +
+
+
장바구니에 담은 것:
+
밀크티, 카페라떼, 카푸치노
+
+ +
+ ); +} + +export default CartPage; diff --git a/src/app/order/[id]/page.tsx b/src/app/order/[id]/page.tsx new file mode 100644 index 0000000..5f68c05 --- /dev/null +++ b/src/app/order/[id]/page.tsx @@ -0,0 +1,9 @@ +"use client"; + +import styles from "@/app/order/order.module.css"; + +function OrderIdPage() { + return
상세페이지
; +} + +export default OrderIdPage; diff --git a/src/app/order/order.module.css b/src/app/order/order.module.css new file mode 100644 index 0000000..7ad737c --- /dev/null +++ b/src/app/order/order.module.css @@ -0,0 +1,96 @@ +.main { + min-height: 100vh; + padding: 2rem; + background: #fff; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.h1 { + font-size: 1.8rem; + font-weight: 700; + margin-bottom: 1rem; + text-align: center; +} + +.menus { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + list-style: none; + width: 100%; + max-width: 200px; + margin: auto; +} + +.menus > li { + width: 100%; +} + +.button { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + outline: none; + border: none; + background: #eaeaea; + font-size: 1.2rem; + padding: .5rem; + border-radius: .5rem; + line-height: 1; +} + +.button:hover { + cursor: pointer; + outline: 2px solid #ddd; +} + +.payment { + max-width: 400px; + width: 100%; + margin: 0 auto; + padding: 1rem; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + background: #fdcf6f; + outline: 0; + border: 0; + font-weight: 700; + border-radius: 0.5rem; +} + +.payment:hover { + cursor: pointer; + outline: 2px solid #f1c469; +} + +.header { + width: 100%; + max-width: 720px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #ddd; +} + +.link { + color: #6e6e71; +} + +.link:hover { + text-decoration: underline; +} + +.text { + font-size: 1.2rem; + text-align: center; + line-height: 1.5; +} diff --git a/src/app/order/page.tsx b/src/app/order/page.tsx new file mode 100644 index 0000000..37ac072 --- /dev/null +++ b/src/app/order/page.tsx @@ -0,0 +1,81 @@ +"use client"; +import * as Sentry from "@sentry/nextjs"; +import { captureException } from "@sentry/nextjs"; +import styles from "./order.module.css"; +import Link from "next/link"; + +function OrderPage() { + const handleClickMenu = (name: string) => { + console.log(`${name}을 주문합니다.`); + Sentry.startSpan({ name: "order-span" }, (span) => { + span?.setTag("menu", name); + console.log('span~~~~~') + console.log(span); + }); + + const transaction = Sentry.startTransaction({ name: "order-transaction" }); + try { + throw new Error(`${name}을 주문하지 못했습니다.`); + } catch (error) { + captureException(error); + } finally { + transaction.finish(); + } + } + + const handleClickCart = async () => { + console.log('장바구니에 담기를 클릭했습니다.'); + + const res = await fetch("/api/sentry-example-api", { method: 'post' }); + if (!res.ok) { + throw new Error("장바구니 API 실패"); + } + } + + return ( +
+
+ + 홈 + +

주문 페이지

+ + 장바구니 + +
+ + +
+ ); +} + +const menus = [ + '아메리카노', + '카푸치노', + '라떼', + '밀크티', +]; + +export default OrderPage; diff --git a/src/app/sentry-example-page/page.jsx b/src/app/sentry-example-page/page.jsx index 4660050..75b510a 100644 --- a/src/app/sentry-example-page/page.jsx +++ b/src/app/sentry-example-page/page.jsx @@ -50,12 +50,12 @@ export default function Page() { }} onClick={() => { Sentry.startSpan({ - name: 'Example Frontend Span', + name: 'New Ignore Span', op: 'test' }, async () => { const res = await fetch("/api/sentry-example-api"); if (!res.ok) { - throw new Error("Sentry Example Frontend Error"); + throw new Error("New Ignore Span Error"); } }); }}