들어가며
회사 프로젝트는 Sentry가 연결돼있다. 작년 여름 기본적인 틀을 구현해두었는데, 이후 프로젝트가 고도화되며 구현된 부분이 제대로 동작하지 않게 됐다. 아직 실 서비스 전이고, 이런저런 핑계로 미뤄두었다가 잠시 틈이 생겨 Sentry를 파보게 됐다.
여러 아티클을 읽어보며, 에러 메시지의 디테일을 중점적으로 구현하였다. 완성 후 조금 더 욕심이 생겨, AI를 연결하면 어떻게 활용할 수 있을지에 대한 맛보기까지 진행하였다.
1부에는 Sentry의 전반적인 설정을 통해 명확히 에러 보기에 대해,
2부에는 ai를 통해 문제점 미리 파악하기
에 대해 작성하려한다.
목차
- Sentry
- Sourcemap
- Sentry 이슈 해상도 높이기
- 노이즈 줄이기
- development/production 구분하기
- 트러블슈팅
1. Sentry
설명처럼, Sentry(센트리)는 에러 발생 시 실시간으로 알림을 전달해주는 도구이다. 간단하게 연결할 수 있지만, 실제 사용에는 몇몇 디테일한 설정을 알고 해주어야 한다.
기존에 설치가 돼있었지만, 글 작성을 위해 설치부터 살펴보려한다.
1.1 Sentry 설치
npx @sentry/wizard@latest -i nextjs 최신 버전 설치

회사 프로젝트는 Nextjs로 구현돼있는데, 공식 홈페이지 보면 sentry는 정말 거의 모든 개발언어에 지원하는 툴같다.
설치시 물어보는 내용이 정~말 많다.
설치 시 나오는 내용 번역
◇ Do you want to continue anyway? → 계속 진행할까요?
◇ Are you using Sentry SaaS or self-hosted Sentry? → Sentry SaaS를 사용하나요, 아니면 자체 호스팅 Sentry를 사용하나요? ** 자체 호스팅 센트리를 사용한다고 설정하면 질문이 달라질 듯 하다.
◇ Do you already have a Sentry account? → 이미 Sentry 계정이 있나요?
● If the browser window didn't open automatically, please open the following link to log into Sentry: → 브라우저 창이 자동으로 열리지 않았다면, 아래 링크를 열어 Sentry에 로그인하세요:
◇ Login complete. → 로그인 완료. ◇ Selected project 프로젝트명 → 선택한 프로젝트: 프로젝트명
◇ Please select your package manager. → 패키지 매니저를 선택하세요.
◇ Installed @sentry/nextjs with NPM. → NPM으로 @sentry/nextjs를 설치했습니다.
◇ Do you want to route Sentry requests in the browser through your Next.js server to avoid ad blockers? → 광고 차단기를 피하기 위해, 브라우저의 Sentry 요청을 Next.js 서버를 통해 라우팅할까요?
◇ Do you want to enable Tracing to track the performance of your application? → 애플리케이션 성능을 추적하기 위해 Tracing을 활성화할까요?
◇ Do you want to enable Session Replay to get a video-like reproduction of errors during a user session? → 사용자 세션 중 오류를 비디오처럼 재현할 수 있도록 Session Replay를 활성화할까요?
** 이러한 안내들이 센트리의 비용을 늘리는 듯. 일단 예를 디폴트로 생각하고 작성했다.
◇ Do you want to enable Logs to send your application logs to Sentry? → 애플리케이션 로그를 Sentry로 보내기 위해 Logs를 활성화할까요?
◆ Created fresh sentry.server.config.ts. → 새 sentry.server.config.ts를 생성했습니다.
◆ Created fresh sentry.edge.config.ts. → 새 sentry.edge.config.ts를 생성했습니다.
◆ Added new src/instrumentation.ts file. → 새 src/instrumentation.ts 파일을 추가했습니다.
◆ Added new src/instrumentation-client.ts file. → 새 src/instrumentation-client.ts 파일을 추가했습니다.
◆ Added Sentry configuration to next.config.js. (you probably want to clean this up a bit!) → next.config.js에 Sentry 설정을 추가했습니다. (추후 정리하는 것을 권장합니다.)
◆ Created src/app/global-error.tsx. → src/app/global-error.tsx를 생성했습니다.
◇ Do you want to create an example page ("/sentry-example-page") to test your Sentry setup? → Sentry 설정을 테스트하기 위한 예제 페이지("/sentry-example-page")를 만들까요?
◆ Created src/app/sentry-example-page/page.tsx. → src/app/sentry-example-page/page.tsx를 생성했습니다.
◆ Created src/app/api/sentry-example-api/route.ts. → src/app/api/sentry-example-api/route.ts를 생성했습니다.
◆ Created .env.sentry-build-plugin with auth token for you to test source map uploading locally. → 로컬에서 소스맵 업로드를 테스트할 수 있도록 auth token이 들어 있는 .env.sentry-build-plugin을 생성했습니다.
◆ Added .env.sentry-build-plugin to .gitignore. → .env.sentry-build-plugin을 .gitignore에 추가했습니다.
◇ Warning: The Sentry SDK is only compatible with Turbopack on Next.js version 15.4.1 or later. → 경고: Sentry SDK는 Next.js 15.4.1 이상에서 Turbopack과 호환됩니다.
◇ Are you using a CI/CD tool to build and deploy your application? → 애플리케이션을 빌드하고 배포하기 위해 CI/CD 도구를 사용하나요?
◇ Add the Sentry authentication token as an environment variable to your CI setup: → CI 설정에 Sentry 인증 토큰을 환경 변수로 추가하세요: SENTRY_AUTH_TOKEN=토큰정보
▲ DO NOT commit this auth token to your repository! → 이 인증 토큰을 저장소에 커밋하지 마세요!
◇ Did you configure CI as shown above? → 위 내용대로 CI를 설정했나요?
◇ Optionally add a project-scoped MCP server configuration for the Sentry MCP? → (선택) Sentry MCP를 위한 프로젝트 범위 MCP 서버 설정을 추가할까요?
** 추후 필요하다면? MCP 설정해놔도 될 듯하다.
◇ Looks like you have Prettier in your project. Do you want to run it on your files? → 프로젝트에 Prettier가 있는 것 같습니다. 파일에 실행할까요?
◇ Formatters have processed your files. → 포매터가 파일을 처리했습니다.
└ Successfully installed the Sentry Next.js SDK! → Sentry Next.js SDK 설치를 완료했습니다! You can validate your setup by (re)starting your dev environment (e.g. npm run dev) and visiting "/sentry-example-page" → 개발 환경을 (재)시작한 뒤(예: npm run dev) "/sentry-example-page"에 방문해서 설정을 검증할 수 있습니다. If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues → 문제가 있으면 여기로 알려주세요: https://github.com/getsentry/sentry-javascript/issues
- 설치하면 기본적으로 4개 파일 생성과 next.config.js에 Sentry 설정이 자동으로 추가 된다.
- Client (
instrumentation-client.ts) — Runs in the browser - Server (
sentry.server.config.ts) — Runs in Node.js - Edge (
sentry.edge.config.ts) — Runs in edge runtimes - src/app/global-error.tsx
- Client (
** 참고로 회사 프로젝트의 경우 서버컴포넌트(SSR)를 사용하지 않는다. Edge는 미들웨어 쪽. Client인 instrumentation-client.ts 을(를) 중점으로 확인하였다.
1.2 instrumentation-client.ts
import * as Sentry from "@sentry/nextjs";
// Sentry.init의 설명에 불필요한 설정은 제외하였음.
Sentry.init({
dsn: ENV.sentryDsn,
environment: nodeEnv,
enabled: !!ENV.sentryDsn,
sampleRate: isProduction ? 1.0 : 0.1,
integrations: [
Sentry.replayIntegration(),
Sentry.consoleLoggingIntegration({ levels: ["warn", "error"] }),
Sentry.browserTracingIntegration(),
],
beforeSend(event, hint) {
const error = hint.originalException;
// 네트워크 에러 태깅
if (error instanceof Error && error.message === "Network Error") {
event.tags = { ...event.tags, error_type: "network" };
}
return event;
},
tracesSampleRate: isProduction ? 1.0 : 0.1,
enableLogs: true,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
sendDefaultPii: true,
});
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
- 해당 파일에 대한 설명은 최종 수정한 내용이라 wizard로 설치한 내용과 다른 부분이 있을 수 있다.
- Sentry.init 에서 사용하는 메서드를 설명하는데 초점을 두기 위함이라 참고하면 된다.
1.2.1 Sentry.init 내부 설명
dsn — Sentry 프로젝트의 고유 주소.
- 에러 데이터를 어느 Sentry 프로젝트로 보낼지 결정하는 엔드포인트 URL.
environment — 이벤트에 환경 태그를 붙임.
- "production", "development" 등으로 구분되어 Sentry 대시보드에서 환경별로 필터링할 수 있다.
enabled — Sentry 초기화 자체를 켜고 끄는 스위치.
!!ENV.sentryDsn- 여기서는 DSN이 없으면 false가 되어 Sentry가 아예 동작하지 않는다.
integrations — Sentry에 추가 기능을 플러그인 형태로 연결하는 배열.
replayIntegration(): 사용자 세션을 비디오처럼 녹화하여 에러 발생 전후 상황을 재현할 수 있게 함.- DOM 변경사항을 기록하는 방식이라 실제 영상 녹화는 아님.
consoleLoggingIntegration({ levels: ["warn", "error"] }):console.warn과console.error호출을 Sentry Logs로 전송.- 에러 이벤트와는 별개로, 로그 레벨 기반 모니터링에 쓰인다.
browserTracingIntegration(): 페이지 로드, 라우트 전환, API 호출 등의 성능 트랜잭션을 자동으로 수집. tracesSampleRate와 함께 동작.
beforeSend(event, hint)— 이벤트가 Sentry 서버로 전송되기 직전에 호출되는 훅.
- 이벤트를 수정하거나 null을 반환하면 전송을 막을 수 있다.
- 여기서는 "Network Error" 메시지를 가진 에러에
error_type: "network"태그를 붙여서 Sentry에서 네트워크 에러만 따로 필터링할 수 있게 하고 있음.
enableLogs — Sentry Logs 기능을 활성화.
- 위의
consoleLoggingIntegration이 실제로 로그를 수집하려면 이 옵션이 true여야 한다.
replaysSessionSampleRate — 일반 세션 리플레이의 샘플링 비율.
- 0.1이면 전체 사용자 세션 중 10%만 리플레이를 녹화.
- 리플레이 데이터는 용량이 크기 때문에 보통 낮게 설정.
replaysOnErrorSampleRate — 에러가 발생한 세션의 리플레이 샘플링 비율.
- 1.0이면 에러가 발생한 세션은 100% 리플레이를 저장.
- 에러 디버깅에 가장 유용한 리플레이이므로 높게 설정하는 게 일반적.
sendDefaultPii — 사용자 IP 주소, 쿠키 등 개인 식별 정보(PII)를 이벤트에 포함할지 여부.
- true로 설정하면 에러 발생 시 어떤 사용자에게서 발생했는지 추적이 쉬워지지만, 개인정보 규정(GDPR 등)에 따라 주의가 필요.
onRouterTransitionStart — Next.js App Router의 라우트 전환을 Sentry 트랜잭션으로 캡처하는 함수.
- Next.js가 라우트 전환 시 이 함수를 호출하면 Sentry가 해당 전환의 성능 데이터를 기록한다.
sampleRate — 에러 이벤트의 샘플링 비율.
captureException이나captureMessage로 전송되는 에러/메시지 중 몇 퍼센트를 실제로 Sentry에 보낼지 결정. 1.0이면 모든 에러를 전송하고, 0.1이면 10%만 전송한다.
tracesSampleRate — 성능 트레이싱(트랜잭션)의 샘플링 비율.
- 페이지 로드, API 호출, 라우트 전환 등의 성능 데이터를 몇 퍼센트나 수집할지 결정. 트레이싱 데이터는 에러보다 양이 훨씬 많기 때문에 비용과 직결된다.
- 실무에서도 production에 0.1 ~ 0.3 정도로 쓴다고 하는데, 이는 실제 사용량이 많아졌을 때 다시 조절할 계획이다.
2. Sourcemap
소스맵(source map)은 변환된 코드와 원본 코드 사이의 매핑 정보를 담은 파일왜 필요할까?
- 오류 발생한 곳이 어디인지 명확하게 파악하기 위해.
프로덕션에 배포되는 JavaScript(또는 CSS)는 보통 원본 그대로가 아니다. 빌드 과정에서 minification(변수명 축소, 공백 제거), bundling(여러 파일 합치기), transpilation(TypeScript → JS, JSX → JS) 등을 거치게 되고 그 결과 브라우저에서 실행되는 코드는 아래와 같은 형태다.
function a(b,c){return b.map(function(d){return d*c+1})}위 함수의 원본은 아래와 같았을 수 있다.
function scaleValues(values: number[], factor: number): number[] {
return values.map((value) => value * factor + 1);
}에러가 발생했을 때 스택 트레이스가 a 함수의 1번째 줄이라고만 알려주면 디버깅이 거의 불가능에 가깝다.
소스맵이 있으면 "원본 scaleValues 함수의 3번째 줄"로 변환해서 보여주게 된다.
- 소스맵은 wizard SDK를 사용하면 자동으로 업로드된다고 하는데, 나는 되지 않았다.
- 짧은 트러블슈팅은 글 제일 하단에 모아두었다.
3. Sentry 이슈 해상도 높이기
수정전, 후의 이미지를 보면 어떻게 변경됐는지 한 눈에 보인다. 이 설정에 대해 알아본다.
수정전

- 어떤 오류인지 알아보기 어려움
수정후

- HTTP 상태 코드, Error Name 확인
- 타이틀, 서브타이틀 구분 으로 가시성 높임!
3.1. Sentry.ts
- 사용자 정보 설정, 현장 컨텍스트 설정, 에러 해상도 높이는 함수에 대해 모아놓은 파일
코드
// utils/sentry.ts
import * as Sentry from "@sentry/nextjs";
import axios from "axios";
/** 로그인 후 사용자 정보 설정 */
export const setSentryUser = (user: { id?: number; email?: string; name?: string }) => {
Sentry.setUser({ id: String(user.id), email: user.email, username: user.name });
};
/** 로그아웃 시 사용자 정보 초기화 */
export const clearSentryUser = () => {
Sentry.setUser(null);
};
/** 현장(construction) 컨텍스트 설정 */
export const setSentryConstruction = (constructionId: string) => {
Sentry.setTag("construction_id", constructionId);
};
class ApiError extends Error {
constructor(name: string, message: string, cause?: unknown) {
super(message);
this.name = name;
this.cause = cause;
}
}
/** 쿼리스트링 제거 + path params를 {id}로 치환 */
const normalizeUrl = (url: string): string => {
try {
return url
.split("?")[0]
.replace(/\/\d+/g, "/{id}");
} catch {
return url;
}
};
/** API 에러를 Sentry에 캡처 */
export const captureApiError = (error: unknown) => {
if (!axios.isAxiosError(error)) {
Sentry.captureException(error);
return;
}
const response = error.response;
const request = error.config;
const endpoint = request?.url || "unknown";
const method = request?.method?.toUpperCase() || "UNKNOWN";
const status = response?.status;
const errorName = response?.data?.errorName;
const normalizedEndpoint = normalizeUrl(endpoint);
// 타이틀: [status errorName] AxiosError
const statusPart = status
? errorName
? `${status} ${errorName}`
: `${status}`
: "NETWORK_ERROR";
const title = `[${statusPart}] ${error.name}`;
// 서브타이틀: METHOD /normalized/path
const subtitle = `${method} ${normalizedEndpoint}`;
Sentry.withScope((scope) => {
scope.setTag("api_endpoint", endpoint);
scope.setTag("api_method", method);
scope.setTag("http_status", String(status || "NETWORK_ERROR"));
if (response?.data?.errorCode) {
scope.setTag("error_code", response.data.errorCode);
}
if (errorName) {
scope.setTag("error_name", errorName);
}
scope.setFingerprint(["api-error", normalizedEndpoint, method, String(status || "NETWORK_ERROR")]);
if (!status || status >= 500) {
scope.setLevel("error");
} else {
scope.setLevel("warning");
}
scope.setExtra("request_url", `${request?.baseURL || ""}${endpoint}`);
scope.setExtra("request_params", request?.params);
scope.setExtra("request_body", request?.data);
scope.setExtra("response_data", response?.data);
Sentry.captureException(new ApiError(title, subtitle, error));
});
};
3.1.1. captureApiError 내 설정된 Sentry 함수/기능 설명
Sentry.withScope(callback)
- 역할: 임시 스코프(Scope)를 생성하여 그 안에서 설정한 태그/extra/fingerprint 등이 해당 이벤트에만
적용되도록 격리
- 왜 필요한가: captureException 옵션의 직접 전달은 fingerprint, level, breadcrumb 설정이 불가능하다.
withScope 없이 글로벌 scope에 설정하면 다른 에러 이벤트에도 영향을 주게 된다.
- 스코프 격리: 콜백 내부에서 설정한 값은 콜백이 끝나면 자동으로 사라져서 다른 이벤트를 오염시키지
않는다.
scope.setTag(key, value)
- 역할: Sentry 이벤트에 태그를 추가. 태그는 인덱싱되어 대시보드에서 검색/필터/알림 조건으로 사용 가능
- 사용 예: api_endpoint, api_method, http_status, error_code, error_name
- extra와의 차이: tag는 검색 가능한 짧은 문자열, extra는 검색 불가능한 상세 데이터
- 태그 정보 차이
설정전

설정후

- 설정이 안됐을 때와 달리,
api_endpoint,api_method,construction_id,error_code,error_name등을 확인 할 수 있다.
scope.setFingerprint(array)
- 역할: Sentry가 에러를 그룹핑하는 기준을 직접 지정한다.
- 왜 필요한가: 기본적으로 Sentry는 스택 트레이스 기준으로 그룹핑된다. API 에러는 모두 axios 인터셉터에서
발생하므로 스택 트레이스가 동일 → 서로 다른 API 에러가 하나의 이슈로 합쳐지는 문제가 있는데, 이를 내가 직접 그룹핑을 설정하여 원하는 그룹별로 이슈를 모으는 것이다.
- 설정값: ["api-error", endpoint, method, status] → 같은 엔드포인트 + 메서드 + 상태코드 조합만 하나의
이슈로 묶임
- 예시: POST /login 403과 GET /drawings 500이 별도 이슈로 분리
- 설정한 fingerprint 확인하기
- 이슈 상세보기에서 JSON을 누르게 되면, 설정한 fingerprint를 확인할 수 있다.

설정전

설정후

- default라 적힌 설정전과 달리, 설정 후엔 오류에 대한 정보가 기록돼 있다.

- 또, 이슈의 하단으로 내려보면
Event Grouping Information탭이 있는데, 여기서도 확인 가능하다.
scope.setLevel(level)
- 역할: 이벤트의 심각도를 설정 (fatal, error, warning, info, debug)
- 사용 방식: 5xx 서버 에러는 error, 4xx 클라이언트 에러는 warning으로 구분해두었다. 에러 종류는 심각도(fatal, error, warning, info, debug)처럼 더 다양하지만, 필요한 에러를 설정하였다.
- 효과: Sentry 대시보드에서 심각도별 필터링, 알림 규칙에서 level 기준 조건 설정 가능하다.
scope.setExtra(key, value)
- 역할: 이벤트에 상세 디버깅 정보를 첨부. 검색은 불가하지만 이벤트 상세 페이지에서 확인 가능
- tag와의 차이: 객체, 배열 등 복잡한 데이터 저장 가능. 검색/필터 불가
- 저장 항목: 요청 URL, 파라미터, 요청 바디, 응답 데이터, 서버 에러 메시지, requestId, 서버 타임스탬프
Sentry.captureException(error)
- 역할: 에러를 Sentry 서버로 전송. withScope 내부에서 호출하면 해당 scope의 모든 설정이 함께 전송됨
4. 노이즈 줄이기
이슈로 잡아내지 않는 조건들은 아래와 같다.
| 조건 | 코드/메서드 | 필터링 이유 |
| 요청 취소 | axios.isCancel(error) | 페이지 이동 시 발생하는 정상 동작 |
| 네트워크 단절 | ERR_NETWORK | 사용자 환경 문제이며 서버/코드 문제 아님 |
| 타임아웃 | ECONNABORTED | 느린 네트워크 환경에서 발생하는 간헐적 노이즈 |
| AbortController 취소 | ERR_CANCELED | isCancel에 잡히지 않는 취소 케이스 커버 |
회사 프로젝트는 axios를 사용중이므로, axiosInstance에 해당 부분을 추가해주었다.
// axiosInstance.ts
const shouldCaptureError = (error: unknown): boolean => {
// 사용자 요청 취소
if (axios.isCancel(error)) return false;
// Axios 에러가 아닌 식별 불가능한 에러는 캡처
if (!axios.isAxiosError(error)) return true;
// 네트워크 단절 및 타임아웃
const code = error.code;
if (code === "ERR_NETWORK" || code === "ECONNABORTED" || code === "ERR_CANCELED") {
return false;
}
return true;
};
...중략
// 응답 인터셉터 - refresh
axiosInstance.interceptors.response.use(
(response) => response,
... 중략
// 401(인증) 외 API 에러를 Sentry에 캡처
if (error.response?.status !== 401 && shouldCaptureError(error)) {
captureApiError(error);
}
return Promise.reject(error);
}
);- 이를 통해, 무분별한 에러를 방지하고 필요한 에러만 제대로 확인할 수 있게 됐다.
5. development / production 구분하기
- 환경변수 추가
- 백엔드에도 개발서버인 도커에 추가를 요청했다.
//.env.development
NEXT_PUBLIC_NODE_ENV=development 설정// instrumentation-client.ts
import { ENV } from "@/utils/env";
// NODE_ENV 또는 NEXT_PUBLIC_NODE_ENV를 확인하여 프로덕션 환경 판단
const nodeEnv = String(ENV.nodeEnv || process.env.NODE_ENV || "development");
const isProduction = nodeEnv === "production" || nodeEnv === "prod";
...
5.1. Slack 알림 설정 구분
dev를 새로이 설정했고, 운영서버와의 알림을 다르게 구분하는 게 좋겠다 생각하여 알림 설정까지 진행하였다.
5.1.1 슬랙 설정
슬랙에 채널 생성 후 , 채널 ID를 필요로 한다.


통합 탭에서는 앱 추가를 통해 Sentry를 넣어둔다.
5.1.2 Sentry Alerts 설정
Sentry Issues → Configure/Alerts 안에서 설정할 수 있다.

- 하지만 기존에 운영에서 사용하는 슬랙 내용이 있어, duplicate하여 작성하였다.


**참고. 대신 넣어줄 때 이 두 개 다 지우고, 특히 # 까지 다 지우고 채널 이름을 넣어줘야한다.
6. 트러블슈팅
Sourcemap 업로드 오류
배경
- 프로젝트에 @sentry/nextjs가 설정되어 있었으나, Sentry에 소스맵이 업로드되지 않아 에러 스택 트레이스가 minified 상태로 표시됐다.
- 앞서 언급했듯 운영서버는 Vercel로 배포 중이다.
문제 원인 분석
1. 환경변수 이름 오류 - 문제: NEXT_PUBLIC_SENTRY_AUTH_TOKEN으로 설정되어 있었음. @sentry/nextjs는 SENTRY_AUTH_TOKEN만 인식한다. 참고로 NEXT_PUBLIC_ 접두사 시 클라이언트에 토큰 노출 위험이 있다. - 해결: SENTRY_AUTH_TOKEN으로 이름 변경 2. CI/Vercel 빌드 중복 - 문제: GitHub Actions CI와 Vercel에서 각각 빌드 → 소스맵 중복 업로드 - 해결: CI에서 제거, Vercel에서만 업로드
- vercel 업로드라는 걸 확인하지 않고 소스맵 업로드라는 부분에 대해 확인하다보니, CI에만 올리는 걸 알려주었다.
- CI설정을 마치고 업로드가 잘 되는 것까지 확인했다.
- 그러다 Ci와 별개로, Vercel에서 다시 빌드해서 처리하지 않나? 생각들었고, 이게 맞았다.
- Ci에서 굳이 소스맵을 다시 하는건 괜히 처리 시간만 더 늘어나는 일이었고, 이 부분은 다시 원상복귀 시켜 해결하였다.
참고. vercel 배포 시 sentry 설정
제목만 보고 어떤 이슈인지 파악하기 1차,2차 수정& fingerprint 그룹핑
- 위에 작성된 captureApiError는 이미 수정해서 작성해뒀지만, 처음에 작성했을때는 없었던 부분이 있다.
그리고 2차로 수정이 됐다.
class ApiError extends Error {
constructor(name: string, message: string, cause?: unknown) {
super(message);
this.name = name;
this.cause = cause;
}
}
/** 쿼리스트링 제거 + path params를 {id}로 치환 */
const normalizeUrl = (url: string): string => {
try {
return url
.split("?")[0]
.replace(/\/\d+/g, "/{id}");
} catch {
return url;
}
};이 변경을 통해 Sentry Feed 제목에서부터 명확한 이슈를 확인할 수 있다.
변경내용
| 항목 | Before | After |
| Sentry 제목 | AxiosError | [GET 403] /api/drawings - INSUFFICIENT_PERMISSION |
| Fingerprint | 쿼리 파라미터 포함 (과도 분산) | 쿼리 파라미터 제거 (정확한 그룹핑) |
| 원본 에러 보존 | 원본 객체 직접 전달 | Error.cause에 보존 |
| 스택 트레이스 | Axios 내부 스택 | ApiError 생성 위치 + 원본 cause |
변경 전

1차 변경

2차 변경

2차 수정
타이틀과 서브타이틀의 조금 더 명시적이고 직관적인 구분
// 타이틀: [status errorName] AxiosError
const statusPart = status
? errorName
? `${status} ${errorName}`
: `${status}`
: "NETWORK_ERROR";
const title = `[${statusPart}] ${error.name}`;
// 서브타이틀: METHOD /normalized/path
const subtitle = `${method} ${normalizedEndpoint}`;- 실제 인입되는 오류를 확인하면 확실하게 구분된다.
development와 production 구분 오류
개발서버와 운영서버 모두 Sentry를 적용하는 게 맞는가? 고민이 있었다. 상의후, 둘 다 사용하되 개발서버는 알럿의 비율을 낮추는 것으로 하기로 했다.
개발서버는 Docker로, 운영서버는 Vercel 배포로 운영되는 프로젝트에서 왜 개발서버의 에러도 Sentry에서는 development가 아닌 production에서 울리는 지 몰랐다.
백엔드 분은 Docker 설정할 때 이미 build:dev로 처리한다 하였고, 그렇기에 process.env.NODE_ENV에 development로 처리 될것이라 하였다.
하지만 해당 부분은 따로 환경변수가 필요했다.
그래서 여태 sentry에는 production에 쌓이고 있었구나!
해당 부분의 환경변수를 수정했다.
//.env.development
NEXT_PUBLIC_NODE_ENV=development 설정// instrumentation-client.ts
import { ENV } from "@/utils/env";
// NODE_ENV 또는 NEXT_PUBLIC_NODE_ENV를 확인하여 프로덕션 환경 판단
const nodeEnv = String(ENV.nodeEnv || process.env.NODE_ENV || "development");
const isProduction = nodeEnv === "production" || nodeEnv === "prod";
...
sampleRate: isProduction ? 1.0 : 0.1,
tracesSampleRate: isProduction ? 1.0 : 0.1,위 설정 후 테스트 결과, development 피드에 10% 의 확률로 오류가 쌓임을 확인할 수 있었다.