Sentry로 애플리케이션 에러 모니터링하기

Sentry로 애플리케이션 에러 모니터링하기

개발 환경에서는 브라우저 콘솔이나 터미널에 에러가 바로 찍히니까 디버깅이 어렵지 않은데요. 프로덕션에 배포하고 나면 이야기가 완전히 달라집니다. 사용자가 “뭔가 안 돼요”라고 제보하면 로그를 뒤지면서 뭐가 문제인지 추적해야 하는데, 이게 생각보다 꽤 고된 작업이거든요 😅

이런 상황에서 빛을 발하는 도구가 바로 Sentry입니다. Sentry는 애플리케이션에서 발생하는 에러를 실시간으로 수집하고, 스택 트레이스와 함께 발생 맥락까지 보여주는 에러 모니터링 서비스인데요. 이번 글에서는 Sentry를 처음 도입하려는 분들을 위해 프로젝트 생성부터 에러 캡처, 성능 모니터링까지 차근차근 살펴보겠습니다.

Sentry란?

Sentry는 오픈소스 기반의 에러 추적(Error Tracking) 플랫폼입니다. JavaScript, Python, Java, Go, Ruby 등 다양한 언어와 React, Next.js, Django, Spring Boot 같은 프레임워크를 지원하는데요. sentry.io에서 클라우드 서비스로 사용할 수도 있고, 직접 서버에 설치해서 자체 호스팅(self-hosted)할 수도 있습니다.

Sentry가 단순한 로깅 도구와 다른 점은 에러를 자동으로 그룹화하고 우선순위를 매겨준다는 것입니다. 같은 에러가 100번 발생하면 100개의 로그를 일일이 보는 게 아니라 하나의 이슈(Issue)로 묶어서 보여주고, 몇 명의 사용자에게 영향을 미쳤는지, 어떤 릴리즈에서 처음 발생했는지도 알 수 있습니다.

프로젝트 생성

먼저 sentry.io에 가입한 후 새 프로젝트를 만들어야 합니다. 대시보드에서 Projects > Create Project를 클릭하면 플랫폼을 선택하는 화면이 나오는데요. 이 글에서는 Node.js를 기준으로 설명하겠습니다.

프로젝트를 생성하면 DSN(Data Source Name)이 발급됩니다. DSN은 https://examplePublicKey@o0.ingest.sentry.io/0 같은 형태의 URL인데요. SDK가 이 주소로 에러 데이터를 전송하기 때문에 초기화할 때 반드시 필요합니다.

DSN은 프로젝트 설정의 Settings > Client Keys (DSN) 메뉴에서도 확인할 수 있습니다.

SDK 설치 및 초기화

Node.js 프로젝트에 Sentry SDK를 설치합니다.

bun add @sentry/node
# 또는
npm install @sentry/node

설치가 끝나면 애플리케이션의 진입점에서 Sentry를 초기화합니다. 가능한 한 다른 모듈을 불러오기 전에 Sentry.init()을 호출하는 것이 좋은데요. 그래야 이후에 발생하는 모든 에러를 빠짐없이 잡아낼 수 있기 때문입니다.

instrument.js
const Sentry = require("@sentry/node");

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  environment: process.env.NODE_ENV || "development",
  release: "my-app@1.0.0",
});

그리고 애플리케이션을 실행할 때 --import 또는 --require 플래그로 이 파일을 먼저 로드합니다.

node --require ./instrument.js app.js

environment 옵션은 development, staging, production 같은 환경을 구분해주고, release 옵션은 배포 버전을 지정합니다. 이 두 가지를 설정해두면 나중에 대시보드에서 특정 환경이나 릴리즈에서 발생한 에러만 필터링할 수 있어서 유용합니다.

DSN 같은 민감한 설정값은 코드에 직접 넣지 않고 환경 변수로 관리하는 것을 권장합니다.

instrument.js
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV || "development",
});

에러 캡처

Sentry SDK를 초기화하면 처리되지 않은 예외(unhandled exception)와 거부된 프로미스(unhandled rejection)는 자동으로 캡처됩니다. 별도 코드를 작성하지 않아도 다음과 같은 에러가 Sentry에 보고됩니다.

// 처리되지 않은 예외 → 자동 캡처
throw new Error("서버에서 예상치 못한 에러 발생!");

// 거부된 프로미스 → 자동 캡처
Promise.reject(new Error("비동기 작업 실패"));

하지만 try...catch로 잡은 에러는 자동으로 보고되지 않으니까 Sentry.captureException()을 직접 호출해야 합니다.

const Sentry = require("@sentry/node");

async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: 사용자 조회 실패`);
    }
    return await response.json();
  } catch (error) {
    Sentry.captureException(error);
    // 에러를 Sentry에 보고한 뒤 적절히 처리
    return null;
  }
}

에러가 아닌 단순 메시지를 기록하고 싶다면 captureMessage()를 사용합니다.

Sentry.captureMessage(
  "사용자가 더 이상 지원하지 않는 API를 호출했습니다",
  "warning",
);

두 번째 인자로 "fatal", "error", "warning", "info", "debug" 같은 심각도 수준을 지정할 수 있습니다.

추가 컨텍스트 설정

에러 메시지와 스택 트레이스만으로는 디버깅에 충분하지 않을 때가 많습니다. Sentry는 에러에 다양한 컨텍스트를 덧붙일 수 있는 방법을 제공하는데요.

우선 사용자 정보를 설정하면 어떤 사용자에게 에러가 발생했는지 파악할 수 있습니다.

Sentry.setUser({
  id: "user-123",
  email: "user@example.com",
  username: "dale",
});

태그(tag)는 에러를 분류하고 검색하는 데 유용합니다. 대시보드에서 태그를 기준으로 에러를 필터링할 수 있거든요.

Sentry.setTag("module", "payment");
Sentry.setTag("api_version", "v2");

브레드크럼(breadcrumb)은 에러가 발생하기 전에 사용자가 어떤 행동을 했는지 시간 순으로 기록합니다. 콘솔 로그, HTTP 요청, DOM 이벤트 같은 것들이 자동으로 수집되는데, 직접 추가할 수도 있습니다.

Sentry.addBreadcrumb({
  category: "auth",
  message: "사용자가 로그인을 시도했습니다",
  level: "info",
});

이렇게 컨텍스트를 풍부하게 설정해두면 에러가 발생했을 때 Sentry 대시보드에서 “누가, 어떤 상황에서, 어떤 순서로 행동하다가 에러를 만났는지”를 한눈에 파악할 수 있습니다.

Express 연동

Express 앱에서 Sentry를 사용할 때는 미들웨어를 통해 좀 더 체계적으로 에러를 처리할 수 있습니다.

app.js
const express = require("express");
const Sentry = require("@sentry/node");

const app = express();

// Sentry 요청 핸들러 — 모든 라우트보다 먼저 등록
Sentry.setupExpressErrorHandler(app);

app.get("/", (req, res) => {
  res.send("Hello World");
});

app.get("/error", (req, res) => {
  throw new Error("의도적인 에러 발생!");
});

app.listen(3000);

setupExpressErrorHandler()를 호출하면 요청별 격리(scope isolation)가 자동으로 설정되어 각 요청의 컨텍스트가 서로 섞이지 않습니다. 또한 라우트 핸들러에서 발생한 에러가 자동으로 Sentry에 보고됩니다.

소스맵 업로드

프로덕션에서 JavaScript 코드는 보통 번들링과 난독화(minification)를 거치기 때문에 에러가 발생해도 스택 트레이스가 알아보기 어렵습니다. 소스맵(source map)을 Sentry에 업로드하면 원본 코드의 파일명과 줄 번호로 스택 트레이스를 볼 수 있습니다.

Sentry CLI를 설치하고 빌드 후 소스맵을 업로드하는 과정은 다음과 같습니다.

bun add -D @sentry/cli
# 또는
npm install --save-dev @sentry/cli
# 인증 토큰 설정
export SENTRY_AUTH_TOKEN="your-auth-token"
export SENTRY_ORG="your-org"
export SENTRY_PROJECT="your-project"

# 소스맵 업로드
npx sentry-cli sourcemaps upload --release my-app@1.0.0 ./dist

Webpack이나 Vite를 사용한다면 빌드 과정에서 자동으로 소스맵을 업로드하는 플러그인을 쓰는 게 더 편리합니다.

bun add -D @sentry/webpack-plugin
# 또는
npm install --save-dev @sentry/webpack-plugin
webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

module.exports = {
  devtool: "source-map",
  plugins: [
    sentryWebpackPlugin({
      authToken: process.env.SENTRY_AUTH_TOKEN,
      org: "your-org",
      project: "your-project",
      release: "my-app@1.0.0",
    }),
  ],
};

Vite를 사용한다면 @sentry/vite-plugin을 대신 사용합니다.

bun add -D @sentry/vite-plugin
# 또는
npm install --save-dev @sentry/vite-plugin
vite.config.js
import { defineConfig } from "vite";
import { sentryVitePlugin } from "@sentry/vite-plugin";

export default defineConfig({
  build: {
    sourcemap: true,
  },
  plugins: [
    sentryVitePlugin({
      authToken: process.env.SENTRY_AUTH_TOKEN,
      org: "your-org",
      project: "your-project",
    }),
  ],
});

성능 모니터링

Sentry는 에러 추적뿐 아니라 성능 모니터링도 지원합니다. tracesSampleRate 옵션을 설정하면 트랜잭션(transaction)을 통해 요청의 전체 처리 시간과 각 구간별 소요 시간을 측정할 수 있습니다.

instrument.js
Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.2, // 전체 요청의 20%를 샘플링
});

tracesSampleRate는 0.0에서 1.0 사이의 값을 받는데요. 1.0이면 모든 트랜잭션을 수집하고, 0.2이면 20%만 샘플링합니다. 프로덕션에서 모든 요청을 추적하면 비용과 성능 부담이 커지니까, 트래픽 규모에 맞게 적절히 조절하는 것이 좋습니다.

Express를 사용하고 있다면 HTTP 요청이 자동으로 트랜잭션으로 기록되는데요. 특정 작업의 성능을 더 세밀하게 측정하고 싶다면 수동으로 스팬(span)을 추가할 수 있습니다.

const result = await Sentry.startSpan(
  { name: "데이터베이스 조회", op: "db.query" },
  async () => {
    return await db.query("SELECT * FROM users WHERE active = true");
  },
);

성능 데이터가 쌓이면 Sentry 대시보드의 Performance 탭에서 느린 요청이나 병목 구간을 시각적으로 확인할 수 있습니다.

알림 설정

에러가 발생할 때마다 대시보드를 들여다보고 있을 수는 없으니, 알림을 설정해서 중요한 에러를 바로 알 수 있게 해야 합니다.

Sentry 대시보드에서 Alerts 메뉴로 들어가면 다양한 조건으로 알림 규칙을 만들 수 있습니다.

  • 새로운 이슈 발생 시 — 처음 보는 에러가 나타나면 즉시 알림
  • 이슈 재발(regression) 시 — 해결했다고 표시한 에러가 다시 발생하면 알림
  • 빈도 기반 — 특정 시간 내에 에러가 N번 이상 발생하면 알림
  • 영향 범위 기반 — 에러가 N명 이상의 사용자에게 영향을 미치면 알림

알림 채널로는 이메일, Slack, Microsoft Teams, PagerDuty 같은 서비스를 연동할 수 있는데요. 팀에서 Slack을 사용하고 있다면 Sentry의 Settings > Integrations에서 Slack 앱을 연결하면 됩니다. 에러 알림이 Slack 채널에 바로 뜨니까 빠르게 대응할 수 있어서 실무에서 거의 필수에 가까운 설정입니다.

릴리즈 관리

배포할 때마다 릴리즈 버전을 지정해두면 어떤 배포에서 에러가 처음 발생했는지 추적할 수 있습니다. 앞서 Sentry.init()에서 release 옵션을 설정하는 것 외에도, Sentry CLI로 릴리즈에 커밋 정보를 연결할 수 있습니다.

# 릴리즈 생성
npx sentry-cli releases new my-app@1.0.0

# 커밋 정보 연결
npx sentry-cli releases set-commits my-app@1.0.0 --auto

# 배포 완료 알림
npx sentry-cli releases finalize my-app@1.0.0
npx sentry-cli releases deploys my-app@1.0.0 new -e production

이렇게 설정하면 Sentry 대시보드에서 각 릴리즈별로 에러 발생 건수와 추이를 확인할 수 있고, 특정 릴리즈에서 갑자기 에러가 늘었는지도 한눈에 볼 수 있습니다. 에러를 유발한 커밋과 작성자까지 보여주기 때문에 “누가 어떤 변경으로 이 에러를 만들었는지”도 파악할 수 있습니다.

샘플링 전략

Sentry는 무료 플랜에서도 매월 일정량의 이벤트를 제공하지만, 트래픽이 많은 서비스에서는 모든 에러와 트랜잭션을 보내면 금방 한도에 도달하게 됩니다. 효율적인 비용 관리를 위해 샘플링 전략을 잘 세우는 것이 중요한데요.

에러 이벤트의 경우 sampleRate 옵션으로 전체 에러 중 몇 퍼센트를 보낼지 결정할 수 있습니다. 다만 에러는 빠뜨리면 안 되는 경우가 많으니까 보통은 1.0(100%)을 유지하고, 성능 트랜잭션의 비율을 낮추는 전략이 일반적입니다.

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  sampleRate: 1.0, // 에러는 전부 수집
  tracesSampleRate: 0.1, // 성능 데이터는 10%만 샘플링
  tracesSampler: ({ name, parentSampled }) => {
    // 헬스체크는 추적하지 않음
    if (name.includes("/health")) {
      return 0;
    }
    // 결제 관련 요청은 전부 추적
    if (name.includes("/payment")) {
      return 1.0;
    }
    // 나머지는 기본 비율 적용
    return 0.1;
  },
});

tracesSampler 함수를 사용하면 요청의 종류에 따라 샘플링 비율을 다르게 적용할 수 있어서 유연합니다. 중요도가 높은 결제 API는 전수 조사하고, 헬스체크처럼 노이즈가 될 수 있는 요청은 아예 제외하는 식으로 운영할 수 있습니다.

마치며

지금까지 Sentry를 사용해서 애플리케이션의 에러를 추적하고 성능을 모니터링하는 방법을 살펴봤습니다. SDK 초기화부터 에러 캡처, 소스맵 업로드, 성능 모니터링, 알림 설정까지 핵심적인 기능을 다뤘는데요.

실제로 Sentry를 도입하면 “사용자가 버그를 제보하기 전에 먼저 에러를 발견하고 대응하는” 경험을 하게 됩니다. 에러가 발생하면 스택 트레이스와 브레드크럼을 통해 재현 없이도 원인을 파악할 수 있고, 릴리즈별 에러 추이를 보면서 배포의 안정성도 확인할 수 있으니까요.

이 글에서는 Node.js를 기준으로 설명했지만 Sentry는 Python, Java, Go, React, Next.js 등 거의 모든 주요 플랫폼을 지원합니다. 공식 문서에서 사용하는 플랫폼에 맞는 가이드를 확인해보시면 좋겠습니다.

AI 코딩 도구를 사용하고 있다면 Sentry MCP 서버를 연결해서 에러 조회와 디버깅을 자연어로 처리하는 방법도 확인해보세요. 애플리케이션 로깅도 함께 정비하면 에러 모니터링의 효과가 배가 되니, 로깅 체계가 아직 잡혀 있지 않다면 같이 신경 쓰는 걸 추천드립니다.

더 자세한 내용은 Sentry for Node.js 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord