들어가며
사이드 프로젝트에서 푸시 알림을 활용한 서비스를 개발하고 있습니다. 서버를 개발하면서, 서버가 새벽에 박살이 나서 밤샘을 하며 서버를 고친 경험이 있습니다. 고치면서 이제는 서버에 문제가 생길 때 왜 문제가 생겼는지 분석해주는 툴을 도입해야겠다고 생각했습니다. 이번 기회에 로그를 분석하는 툴을 도입하고, 서버가 문제가 생길 시 슬랙으로 알림이 오도록 구성하고 싶었습니다. 이 글은 제가 어떻게 Sentry와 Slack을 활용하여 서비스 장애에 대처할 수 있게 설정했는지 설명하는 글입니다.
Sentry
Sentry는 애플리케이션에서 오류가 발생하면 알려주는 에러 트래킹 서비스입니다. (무료 혹은 유료) 클라이언트의 오류 발생 시 메일을 보내주고, 슬랙과 연동하면 슬랙 메시지를 통해 오류 발생과 해당 오류에 대한 정보 파악이 가능합니다. Javascript, vue.js, node.js, java, python 등의 다양한 언어, 프레임워크, 라이브러리를 지원하여 여러 프로젝트의 이슈를 한 곳에서 관리함으로써 에러 모니터링을 일원화할 수 있습니다. 이런 sentry를 NestJS에 적용시켜보겠습니다.
Sentry 설정
먼저 Sentry를 활용하기 위해 먼저 Sentry에 들어가서 가입하겠습니다. 특정 방식으로 가입하면 다음과 같이 나옵니다.
저는 다음과 같이 test로 작성하고 Create Organization을 클릭하겠습니다.
그럼 위와 같이 나오는데, NestJS를 활용한다면, NODE.JS를 활용하는 것이기에 NODE.JS를 선택합니다.
그다음 Project name을 입력하고 입력이 모두 끝났으면 Create Project를 클릭합니다.
그럼 다음과 같은 화면이 뜨는데, 프로젝트에서 필요한 것은 Sentry.init에서 dsn에 적혀있는 문자열입니다. 이를 복사하여 프로젝트 환경변수에 추가합니다. 그 후 Take me to Issues 버튼을 클릭합니다.
그럼 다음과 같이 설정되었다면 이제 Sentry를 사용할 준비가 끝났습니다. 그럼 이제 NestJS로 돌아가서, Sentry에 대해 설정하겠습니다.
NestJS Sentry 설정
NestJS에서 Sentry를 설정하기 위해 관련 라이브러리를 설치하겠습니다.
$ npm i @sentry/node nest-raven
그 후 main.ts에 sentry를 연결하겠습니다.
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as Sentry from '@sentry/node';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
await app.listen(3000);
}
bootstrap();
위와 같이 설정했다면, Sentry를 사용할 준비가 끝났습니다. 그럼 에러 코드가 출력되면 Sentey에서 확인하면서, 동시에 Slack에서도 확인하고 싶다면 어떻게 해야 할까요? 여기서는 Interceptor를 활용하여 알아보겠습니다.
Slack Hook 설정
Slack Hook 설정을 위해 먼저 앱을 클릭합니다.
앱은 더 보기를 클릭하면 나옵니다.
그 후 입력창에 incoming을 입력하면 incoming WebHooks이 나옵니다. 이를 추가합니다.
그럼 다음과 같이 나오는데, Slack에 추가 버튼을 누릅니다.
이 화면이 나오면, 채널 선택을 눌러서, 웹 훅으로 알림을 받고 싶은 채널을 선택합니다.
여기서 나온 웹 훅 URL을 복사하여 환경변수에 추가합니다. 여기까지 설정했다면, 이제 NestJS에서 Slack을 활용하기 위한 라이브러리를 설치합니다.
$ npm i @slack/webhook
다음과 같이 라이브러리를 설치하고, 아래의 인터셉터를 구성하여 main.ts에 app.useGlobalInterceptors에 추가합니다.
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as Sentry from '@sentry/node';
import { WebhookInterceptor } from './common/webhook.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
app.useGlobalInterceptors(new WebhookInterceptor());
await app.listen(3000);
}
bootstrap();
webhook.interceptor
import {CallHandler, ExecutionContext, Injectable, NestInterceptor} from '@nestjs/common';
import {catchError} from 'rxjs/operators';
import {IncomingWebhook} from '@slack/client';
import slackConfig from '../config/slack.config';
import {of} from 'rxjs';
import * as Sentry from '@sentry/minimal';
@Injectable()
export class SentryInterceptor implements NestInterceptor {
intercept(_: ExecutionContext, next: CallHandler) {
return next.handle().pipe(
catchError(error => {
Sentry.captureException(error);
const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK);
webhook.send({
attachments: [
{
color: 'danger',
text: '🚨ah-ha-api-server 버그 발생🚨',
fields: [
{
title: `Request Message: ${error.message}`,
value: error.stack,
short: false,
},
],
ts: Math.floor(new Date().getTime() / 1000).toString(),
},
],
});
return of(error);
}),
);
}
}
const webhook 변수에 new IncomingWebhook 안에 Slack Webhook 환경변수를 주입한 객체를 값으로 할당합니다. 이렇게 설정하면 에러가 발생할 경우 다음과 같이 알림이 전달됩니다.
저희 팀의 경우 Slack으로 오는 문구를 다음과 같이 커스터마이징 했습니다. 위의 경우처럼 Slack으로 오는 에러 메시지를 자유롭게 변경할 수 있습니다.
Sentry를 처음 사용하는 경우 무료로 사용하면 됩니다. 무료 사용 시 제한 사항은 다음과 같습니다.
[무료 사용 시 제한 사항]
- 멤버 수 제한 : 하나의 계정으로 로그인해야 해당 이슈들을 조회할 수 있습니다. (유료 사용의 경우 무제한)
- 에러 수 제한 : 무료 사용 시 5,000개로 제한 (유료 사용 시 100,000개 이상 ~ )
- 히스토리 저장 : 오류에 대한 히스토리를 30일만 저장 (유료 사용 시 90일)
- 기타 소소한 추가 기능들은 링크를 통해 확인 가능합니다. Sentry 유료 정책
마치며
앞으로도 팀의 발전을 돕는 개발자가 되기 위해 노력하려 합니다. 팀에 필요한 부분이 무엇일지 고민하면서, 팀에 도움이 된다면, 열심히 공부해서 실무에 적용할 수 있는 개발자가 되기 위해 노력하고 싶습니다. 팀의 성장에 기여할 수 있는 개발자가 되겠습니다.
참고 및 출처
'Project > 서버 개발' 카테고리의 다른 글
[Project] 프로젝트 삽질기26 (feat Redis 설정) (0) | 2022.05.08 |
---|---|
[Project] 프로젝트 삽질기25 (feat Transaction) (0) | 2022.05.05 |
[Project] 프로젝트 삽질기23 (feat Enum) (0) | 2022.04.22 |
[Project] 프로젝트 삽질기22 (feat Exception) (0) | 2022.04.21 |
[Project] 프로젝트 삽질기21 (feat 모노레포) (0) | 2022.04.19 |