서비스를 배포하고 지속적으로 유지보수하려면 발생하는 에러를 신속하게 분석하고 대응할 수 있어야 합니다.
하지만 개발 환경에서 볼 수 있는 에러는 한정적입니다. 다양한 기기와 버전, 환경을 모두 테스트하는 것은 불가능에 가깝기 때문입니다. 우리는 사용자가 어떤 기기를 사용하고, 어떤 소프트웨어와 브라우저 버전을 사용하고 있는지 예측할 수 없습니다. 게다가 사용자에게 이를 직접 묻는 것도 현실적으로 어려운 일입니다. 이러한 예기치 못한 상황에서 발생하는 에러를 분석하고 대응하려면 모니터링 툴이 필요합니다.
프론트엔드에서 오류를 탐지하는 다양한 모니터링 툴이 존재하는데, 그 중에서도 널리 사용되는 툴인 Sentry에 대해 소개해보겠습니다.
오류를 탐지하기 위한 프론트엔드 모니터링 툴은 여러 가지가 존재합니다. 그 중 널리 사용되고 있는 Sentry에 대해서 소개해보고자 합니다.
Sentry는 실시간 로그 수집 및 분석 도구이자 모니터링 플랫폼으로, 로그를 통해 발생한 이벤트에 대한 다양한 정보를 제공하고 이를 효과적으로 분석할 수 있게 돕습니다. 이벤트가 발생한 시점과 타임라인을 확인할 수 있으며, 필요한 경우 알림을 설정하여 실시간으로 오류를 추적할 수 있습니다. Sentry는 단순히 로그를 수집하는 것에 그치지 않고, 이를 시각적으로 분석할 수 있는 도구도 제공합니다. 또한, 다양한 플랫폼과 통합이 가능합니다.
다음은 Sentry의 주요 특징에 대해 간략하게 설명한 내용입니다.
이벤트 로그 상세 정보 제공
Sentry는 이벤트가 발생했을 때 관련된 다양한 정보를 제공합니다.
또한, 기본적으로 제공되는 정보 외에도 Context 기능을 사용해 특정 이벤트에 대한 추가 정보를 수집할 수 있습니다.
유사 오류 통합
Sentry는 이슈 그룹화(Issue Grouping) 기능을 통해 유사한 이벤트 로그를 하나의 이슈로 묶어 관리할 수 있습니다. 이를 통해 비슷한 오류를 빠르게 식별하고 추적할 수 있어 매우 유용합니다.
다양한 알림 채널 지원
발생한 이슈에 대해 실시간 알림을 받을 수 있는 다양한 채널을 지원합니다. 예를 들어, Slack, Teams, Jira, GitHub 등과 연동하여 알림을 받을 수 있습니다. 현재 제 회사에서는 Slack을 통해 실시간으로 오류를 추적하고 있습니다.
제 회사에서는 프론트엔드 기술 스택으로 주로 React를 사용하고 있습니다. 이번에는 React 환경에서 Sentry를 설정하고 데이터를 쌓을 수 있는 기본 기능에 대해 살펴보겠습니다.
@sentry/react
, @sentry/tracing
패키지를 설치해야 합니다.# npm 사용
npm install --save @sentry/react @sentry/tracing
# yarn 사용
yarn add @sentry/react @sentry/tracing
@sentry/browser
에서 제공되는 모든 메소드는 @sentry/react
에서도 사용할 수 있습니다.import React from 'react';
import ReactDOM from 'react-dom';
import * as Sentry from '@sentry/react';
import { BrowserTracing } from '@sentry/tracing';
import App from './App';
Sentry.init({
dsn: 'dsn key',
release: 'release version',
environment: 'production',
normalizeDepth: 6,
integrations: [
new Sentry.Integrations.Breadcrumbs({ console: true }),
new BrowserTracing(),
],
});
ReactDOM.render(<App />, document.getElementById('root'));
Sentry 설정을 위한 주요 항목은 다음과 같습니다:dsn: 이벤트를 전송할 때 사용하는 식별 키
release: 애플리케이션 버전 (보통 package.json
에 명시된 버전 사용, 이는 버전별 오류 추적을 용이하게 합니다)
environment: 애플리케이션 환경 (예: dev, production 등)
normalizeDepth: 컨텍스트 데이터를 주어진 깊이까지 정규화 (기본값: 3)
integrations: 플랫폼 SDK와의 통합 설정 (React에서는 react-router
통합 설정 가능) 이벤트를 전송하기 전 선택적으로 데이터를 수정할 수 있는 beforeSend
와 같은 옵션도 제공합니다.
추가적인 설정 옵션은 공식 문서에서 확인할 수 있습니다.
추가적으로 React SDK는 자동으로 JavaScript 오류를 탐지하고 Sentry로 전송할 수 있도록 Error Boundary 컴포넌트를 제공하며 다음과 같이 사용할 수 있습니다.
import React from 'react';
import * as Sentry from '@Sentry/react';
<Sentry.ErrorBoundary
fallback={<p>에러가 발생하였습니다. 잠시 후 다시 시도해주세요.</p>}
>
<Example />
</Sentry.ErrorBoundary>;
이제 Sentry를 통해 에러를 전송해 봅시다.
captureException
try-catch
구문과 함께 사용하여 예외를 처리한 뒤, 그 예외를 Sentry로 전송합니다.try {
// 오류가 발생할 수 있는 코드
throw new Error("Something went wrong!");
} catch (error) {
Sentry.captureException(error);
}
captureMessage
Sentry.captureMessage("This is a custom warning message", "warning");
captureMessage
는 예외가 아니므로, 메시지 자체와 그 수준(level)을 지정할 수 있습니다."info"
, "warning"
, "error"
등의 메시지 레벨을 설정할 수 있습니다.Sentry의 전송 API는 위의 예시 외에도 다양한 추가 기능을 제공합니다.
API | 설명 |
---|---|
captureEvent | 사용자 정의 이벤트 객체를 직접 전송 |
captureException | 예외 객체를 전송 (기존) |
captureMessage | 단순 메시지 전송 (기존) |
addBreadcrumb | 로그나 이벤트에 대한 추가적인 정보를 기록 |
setUser | 사용자 정보를 설정하여 해당 사용자에 대한 이벤트 추적 |
setTag | 이벤트에 태그를 추가하여 필터링을 용이하게 함 |
setContext | 이벤트에 추가적인 컨텍스트(상황 정보)를 설정 |
Sentry는 scope
단위로 이벤트 데이터를 관리합니다. 이벤트가 전송되면 해당 이벤트의 데이터를 현재 scope
의 추가 정보와 병합합니다.
User Information (사용자 정보)
Sentry.configureScope(scope => {
scope.setUser({ id: '123', email: 'user@example.com' });
});
Tags (태그)
Sentry.configureScope(scope => {
scope.setTag('feature', 'checkout');
});
Extra Data (추가 데이터)
Sentry.configureScope(scope => {
scope.setExtra('app_version', '1.0.0');
});
Breadcrumbs (브레드크럼)
Sentry.addBreadcrumb({
message: 'User clicked checkout button',
level: 'info',
});
Clearing Scope
Sentry.configureScope(scope => {
scope.setUser(null); // 사용자 정보 초기화
});
기본적으로 Sentry.configureScope()
는 전역적으로 설정을 변경하지만, withScope
는 특정 블록 내에서만 scope
설정을 적용하도록 제한할 수 있습니다. 이를 통해 에러를 발생시킬 때 해당 블록 내에서만 scope
설정이 영향을 미치게 됩니다.
withScope
를 사용하면 한 번의 에러를 기록할 때, 그 에러에 대한 user
, tags
, extra data
등을 한정적으로 설정할 수 있습니다. 이 설정은 블록 내에서만 유효하고, 블록을 벗어나면 자동으로 이전 상태로 돌아갑니다.Sentry.withScope(scope => {
// 이 블록 내에서만 scope가 설정됩니다.
scope.setUser({ id: '123', email: 'user@example.com' });
scope.setTag('page', 'checkout');
// 범위 내에서 발생한 이벤트는 설정된 사용자 정보와 태그를 포함
Sentry.captureException(new Error('Something went wrong'));
});
Sentry에서는 기본적인 정보 외에도 custom context를 추가하여, 개발자가 정의한 특정 정보도 함께 전달할 수 있습니다. 예를 들어, 특정 API 호출의 응답 데이터나 사용자가 선택한 옵션을 기록하는 등의 작업이 가능합니다.
Sentry.configureScope(scope => {
scope.setContext('api_call', {
endpoint: '/users',
method: 'POST',
status: 'failed',
});
});
에러나 이벤트에 사용자가 정의한 태그를 추가하여, 나중에 Sentry 대시보드에서 보다 세부적으로 필터링하거나 분석할 수 있도록 돕는 기능입니다. 태그는 주로 이벤트나 에러의 특징적인 속성을 나타내는 데 사용되며, 이를 통해 에러를 그룹화하거나, 특정 조건에 맞는 에러들을 쉽게 검색하고 추적할 수 있습니다.
scope.setTag()
를 사용하여 커스텀 태그를 설정할 수 있습니다. 예를 들어, 특정 페이지나 기능에서 발생한 에러를 태그로 기록할 수 있습니다.
Sentry.configureScope(scope => {
scope.setTag('feature', 'checkout');
});
Sentry에서는 이벤트가 fingerprint를 기반으로 자동 그룹화됩니다. fingerprint는 stacktrace, exception, message 등 정보를 바탕으로 생성되며, 같은 fingerprint를 가진 이벤트는 하나의 이슈로 묶입니다. 그러나 때때로 이슈 그룹화가 예상과 다르게 이루어질 수 있습니다. 예를 들어, 동일한 API에서 발생한 400, 404, 500 오류는 요청 URI가 같으면 하나의 이슈로 묶입니다. 이를 해결하려면 HTTP method, status, url을 fingerprint 조건으로 설정하여 각각의 오류를 독립적인 이슈로 그룹화할 수 있습니다.
import * as Sentry from '@Sentry/react';
const { method, url } = error.config; // axios의 error객체
const { status } = error.response; // axios의 error객체
Sentry.setFingerprint([method, status, url]);
Sentry는 이 외에도 Level과 같이 태깅을 하여 에러를 그룹화 하거나, Error 객체를 확장하여 사용자화할 수 있습니다. 이렇게 이슈를 그룹화 하면 동일한 에러 객체로 생성되어 전송된 이벤트들끼리 그룹화 되어 해당 이슈의 빈도나 발생 케이스를 파악할 수 있게 됩니다.
발생하는 모든 이벤트를 모니터링하고 알람을 받는 것은 매우 비생산적인 일입니다. 따라서 서비스의 성격에 따라 알람을 받기 원하는 조건과 임계치(threshold)를 잘 설정해야 합니다. 위에서 소개했던 기능들은 알람에서 알람 조건을 설정할 때 유용하게 사용될 수 있습니다.
API 오류 중 500 에러에 대해서만 알람을 받고 싶다면 tag
과 level
기능을 이용하여 설정해볼 수 있습니다. 예를 들어 API 500 에러에 대해 level
을 Error
로, 이벤트의 tag
를 api
로 설정하면 관련한 이벤트를 추려낼 수가 있게 됩니다. 이 알람 조건은 이벤트나 이슈 검색 조건으로 활용될 수도 있습니다.
또한 API 오류의 경우 사용량이 많은 애플리케이션에서는 HTTP status가 400, 401, 404, 204인 이벤트 수집은 큰 도움이 되지 않을 수 있습니다. 그러나 사용자 경험을 감지하는데 유용하다고 생각된다면 internal server error(500) 이벤트를 수집하고 분석하는 것이 많은 도움이 될 수 있습니다.