Spring BootMonitoringBackendSentry

에러 추적의 자동화: Sentry 도입과 Trace ID를 활용한 장애 대응

서버 로그만 뒤적이던 시절을 벗어나, Sentry를 도입하고 프론트엔드부터 백엔드까지 이어지는 Trace ID를 통해 장애를 한눈에 파악하게 된 과정을 회고합니다.

Srue2026년 4월 5일
에러 추적의 자동화: Sentry 도입과 Trace ID를 활용한 장애 대응

이전에 남겼던 Spring Trace ID Filter Interceptor 글에서, 클라이언트 요청마다 고유한 Trace ID(혹은 Correlation ID)를 발급해 로깅하는 방법을 정리했습니다.

당시만 해도 파일로 떨어진 application.log를 서버에 SSH로 접속해 grep 명령어로 검색하는 수준이었습니다. 서비스 규모가 작았을 때는 큰 문제가 아니었지만, MSA까지는 아니더라도 API 통신 횟수가 늘고 복잡도가 올라가자 터미널을 열고 로그를 쫓아다니는 시간 자체가 고통으로 다가왔습니다.

오늘은 이런 고전적인 에러 추적 방식에서 벗어나, Sentry 모니터링 도구를 도입하고 생명줄 같은 Trace ID를 연동하여 트러블슈팅의 패러다임을 바꾼 삽질 기록입니다.

에러, 어디서 터졌는지 누가 알려줄래?

장애가 났을 때 가장 피곤한 상황은 에러가 났다는 제보가 슬랙으로 들어왔는데, 그 에러가 "왜", "어느 상황에서" 발생했는지 클라이언트 쪽에 남은 정보가 없을 때입니다.

에러 추적을 일원화하기 위해 Sentry(센트리) 모니터링을 도입하기로 결정했습니다. Sentry는 예외(Exception)가 발생하는 순간 콜스택과 각종 메타데이터를 포착해 자체 대시보드로 쏴주는 강력한 서비스입니다.

Sentry + Spring Boot 연동 기초

Spring Boot 환경에서 Sentry를 붙이는 건 사실 허무할 정도로 쉽습니다.

build.gradle
dependencies {
    implementation 'io.sentry:sentry-spring-boot-starter:7.X.X'
}
application.yml
sentry:
  dsn: https://{YOUR_DSN}@o{YOUR_ORG}.ingest.sentry.io/{YOUR_PROJECT}
  traces-sample-rate: 1.0
  exception-resolver-order: -2147483647

일단 이렇게 달아두기만 해도 서버에서 Uncaught Exception이 발생할 때마다 슬랙 채널을 통해 알림이 날아오는 신세계를 맛볼 수 있습니다.

진정한 'Context'의 완성: Trace ID 태깅

하지만 여기서 만족할 수 없었습니다. 에러 콜스택만 보면 어느 코드 라인에서 터진 건진 알 수 있지만, "이 요청을 보낸 유저가 누구고, 당시 Payload가 뭐였는지"를 알기엔 요원했기 때문입니다.

이때 빛을 발하는 것이 기존에 만들어 두었던 MDC(Mapped Diagnostic Context) 안의 Trace ID였습니다.

에러가 Sentry로 발송되기 직전, Sentry의 EventProcessor를 가로채서 우리가 가지고 있는 Trace-ID를 Sentry 이벤트의 태그(Tag)로 꽂아 넣기로 했습니다.

SentryEventProcessor.java
@Component
public class SentryEventProcessor implements EventProcessor {
 
    @Override
    public SentryEvent process(SentryEvent event, Hint hint) {
        String traceId = MDC.get("traceId");
        if (StringUtils.hasText(traceId)) {
            // Sentry 뷰에서 필터링 가능하도록 태그 추가
            event.setTag("trace_id", traceId);
        }
 
        String userId = SecurityContextHolder.getContext().getAuthentication().getName();
        if (StringUtils.hasText(userId) && !"anonymousUser".equals(userId)) {
            // 영향받은 유저 파악을 위해 유저 정보 세팅
            User user = new User();
            user.setId(userId);
            event.setUser(user);
        }
 
        return event;
    }
}

에러 추적의 흐름이 변하다

위 작업을 거치고 나니 장애 대응 시나리오가 180도 바뀌었습니다.

과거:

  1. 고객문의 접수: "결제가 안 돼요."
  2. 서버 접속 후 app.log를 열람.
  3. 고객 아이디로 grep 시도.
  4. 동시간대 로그 여러 줄을 눈으로 스캐닝하며 원인 추정.

현재:

  1. 에러가 터지는 즉시 Slack 알림 도착. (클릭하면 Sentry로 연결)
  2. Sentry 화면에서 에러의 콜스택, 발생 빈도, trace_id, 발생한 user_id 확인.
  3. 해당 trace_id를 기반으로 정상적인 흐름의 파라미터가 어떻게 들어왔는지 확인 필요 시에만 터미널이나 로깅 시스템(ex. ELK)을 짧게 조회.

이제 프론트엔드 파트에서도 예외 화면 노출 시 사용자에게 Trace ID를 표기해주어 문의 접수 시 함께 전달받을 수 있도록 변경되었습니다.

단순히 알림 솔루션 하나 붙인 게 아니라, 팀 내 장애 커뮤니케이션 비용 자체를 혁신적으로 줄인 경험이었습니다.