Next.js와 Slack Webhooks로 실시간 에러 알림 시스템 만들기

해달·2024년 8월 25일
post-thumbnail

에러를 로그하는 방법에는 여러가지가 있겠지만 간단하고 빠른 방법 중 슬랙을 이용하는 방법이 있다.
간단한 프로젝트에서는 빠르고 가볍게 시도해 볼 수 있다고 생각해서 에러 봇을 만들기로 결정했다!

이번 달 초에 작성하려고 임시저장에 넣어두고 틈틈이 고치다가,,
오늘 초안 다 작성하고 GPT의 도움을 받아 첨삭했습니다. 🌝

혹시 이 글에서 틀리거나 개선할 부분이 있다면, 말씀해 주세요! 😊

TL;DR

  • Slack Webhook + Next.js API + error.tsx을 이용한 클라이언트 에러 처리
  • Next.js 버전: 14.2.5
  • Example Repository : GitHub
  1. 슬랙 설정
  2. error.tsx 파일과 환경변수 설정
  3. Next.js POST API 작성 및 Slack 메시지 구성
  4. Vercel 환경변수 설정 (선택사항)

들어가기 앞서,

Slack 메시지를 받을 워크스페이스와 채널을 먼저 생성해 주세요.

1. Slack Webhooks

1.1 슬랙 웹훅이란?

Slack Webhooks는 외부 애플리케이션에서 Slack 채널로 메시지를 전송하기 위한 간단한 HTTP 요청 방식입니다. 이 방법은 주로 외부 시스템의 이벤트를 Slack에 알리고자 할 때 사용됩니다. 웹훅 URL을 통해 JSON 형식의 데이터를 Slack 채널로 전송할 수 있으며, 이를 통해 알림, 경고, 로그 등을 Slack에 통합할 수 있습니다.

1.2 Incoming Webhooks

Incoming Webhooks는 외부 애플리케이션에서 Slack 채널로 메시지를 전송할 수 있는 가장 간단한 방법 중 하나입니다. 특정 채널로 메시지를 전송하기 위해 Slack에서 발급된 URL을 사용합니다. 이 URL로 HTTP POST 요청을 보내면 해당 요청에 포함된 메시지가 지정된 Slack 채널로 전송됩니다.

1.3 Create New App

Slack 웹훅을 사용하기 위해 앱을 생성해야 합니다. Slack API 페이지에 접속하여 "Create New App"을 클릭하세요.

https://api.slack.com/apps

1.4 From scratch

"From Scratch" 옵션을 선택하여 슬랙 앱을 처음부터 새롭게 구성할 수 있습니다.

🤖 GPT 팁: "From scratch" 옵션은 기존 템플릿 없이 처음부터 앱을 구성해야 할 때 유용합니다.

Bot 이름, workspace 지정

1.5 Bot 이름과 Workspace 지정

봇의 이름을 정하고 메시지를 받을 워크스페이스를 선택하세요.

Bot 이름, workspace 지정

슬랙 봇 생성 완료!

1.6 Incoming Webhooks 활성화

앱이 생성되면 "Incoming Webhooks"를 활성화하세요.

1.7 Add New Webhook to Workspace

Incoming Webhooks 활성화 후, 하단의 "Add New Webhook to Workspace" 버튼을 클릭하세요.

Bot 이름, workspace 지정

1.8 Workspace 연결

이 화면에서 원하는 채널을 선택하여 Slack Webhook을 연결할 수 있습니다.

Bot 이름, workspace 지정

1.9 Webhook URL 발급 완료

URL이 정상적으로 발급되었으며, 보안을 위해 숨겼습니다.

sample curl request to post to a channel

터미널에서 테스트하여 Webhook이 제대로 동작하는지 확인할 수 있습니다.
Bot 이름, workspace 지정

메시지가 정상적으로 전송되면, 아래와 같이 Slack에서 확인할 수 있습니다.
Bot 이름, workspace 지정

그러면 이제 슬랙쪽은 준비가 끝났으니 에러를 감지해 봇으로 메세지 보내는 코드를 작성해주면 됩니다.


2. Next.js 에러 처리하기

의도치 않은 에러 발생 시 사용자에게 표시될 페이지와 해당 페이지에서 취할 액션을 error.tsx 파일에서 정의할 수 있습니다.

에러 바운더리는 React 컴포넌트 트리에서 자식 컴포넌트에서 발생한 오류를 잡아내어 전체 애플리케이션의 충돌을 방지합니다.

Next.js에서는 error.js 또는 error.tsx 파일을 사용하여 이러한 오류를 처리할 수 있으며, 에러 바운더리로 감싸진 영역에서 발생한 모든 오류는 이 파일에서 정의된 페이지로 리디렉션됩니다.

2.1 환경변수 설정

.env.local 파일에 환경변수를 추가하세요. (이 단계는 선택사항입니다)

NEXT_PUBLIC_API_BASE_URL='http://localhost:3000'
NEXT_PUBLIC_SLACK_WEBHOOK_URL=''

2.2 error.tsx 파일 작성

에러 페이지가 트리거 될 때, useEffect로 만든 API handler를 호출하여 에러 메시지와 pathname을 Slack으로 전송합니다.

// ... (다른코드)

  useEffect(() => {
    const reportError = async () => {
      try {
        await ky.post('/api/slack', {
          json: {
            location: pathName,
            message: error.message,
          },
        });
        setIsHttpRequestSuccess(true);
      } catch (err) {
        setIsHttpRequestSuccess(false);
      }
    };

    reportError();
  }, [error.message, pathName]);

이제 error.tsx 파일 작업이 완료되었습니다.
다음으로는 api/slack handler를 작성해야 합니다.

2.2.1 테스트용 에러 발생 코드 작성하기 (선택사항)

에러 발생 시 테스트할 수 있도록 임시로 에러를 발생시키는 코드를 작성할 수 있습니다.

이런식으로 임시 코드만들어놓으면 에러가 발생하면서 슬랙 메세지 발송을 테스트해볼 수 있다.

  const [count, setCount] = useState(3);

  const throwCounterClickHandler = (e: any) => {
    e.preventDefault();
    setCount(count - 1);
  };

  if (count <= 0) {
    throw new Error('Counter threw an error!');
  }

3. Next.js API 붙이기

3.1 POST API 만들기

이제 Slack Webhook으로 요청을 보내기 위한 POST API를 작성합니다.

저는 axios 대신 ky를 사용했습니다.
Ky는 경량화된 HTTP 클라이언트 라이브러리로, 더 간단하게 사용할 수 있습니다.

// app/api/slack/route.ts

import { getErrorResponse } from '@/app/utils/helper'; 
import { config } from '../../../../config';
import ky from 'ky';

type BodyType = {
  location: string;
  message: string;
};

export async function POST(req: Request) {
  if (req.method !== 'POST') {
    return getErrorResponse(405, 'Method Not Allowed');
  }

  try {
	// 전달받은 에러메세지와 에러 발생 위치 값을 받아옵니다.
    const { message, location } = (await req.json()) as BodyType;
    
    // Slack 웹훅 URL 설정
    const slackWebhookUrl = config.slack.monitorUrl || '';

    if (!slackWebhookUrl) {
      return getErrorResponse(500, 'Internal Server Error');
    }
	
    /*
    여기서부터는 웹훅을 이용해 Slack 메시지로 받고 싶은 데이터를 자유롭게 구성할 수 있습니다.
    저는 userAgent, 접속기종, 모바일 여부 등을 추가했습니다.
    */
    
    const userAgent = req.headers.get('user-agent') || 'no agent';
    const mobile =
      /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
        userAgent
      );
    const isMobile = mobile ? 'mobile' : 'web';
    
    // 원하는 형태로 작성하기
    const md_text = {...}

   
    await ky.post(slackWebhookUrl, {
      json: md_text
    });

    return new Response('Hello, world!');
  } catch (error) {
    console.log('error', error);
    return getErrorResponse(500, 'Internal Server Error');
  }
}

3.2 Slack 메시지 확인하기

요청이 제대로 도착하면 아래와 같이 메시지가 Slack에 도착합니다.
모든 필드를 다 사용하지는 않지만, 표시되는 예를 보여주기 위해 모두 작성했습니다.

{
	text: '*중요*: `코드`는 기본적인 마크다운 형식입니다.',
        attachments: [
          {
            pretext: '메시지 전에 나타나는 텍스트',
            color: '#feb958', // 첨부 파일의 왼쪽에 나타나는 색상 바의 색
            author_name: '문선영',
            author_link: 'https://avatars.githubusercontent.com/u/85778994?v=4',
            // 첫번째 조그마한 썸네일
            author_icon: 'https://velog.velcdn.com/images/sssssssssy/post/f7383259-1e51-4cb2-b958-c5b85956fb45/image.png',

            title: '제목 : 문선영 블로그',
            title_link: 'https://velog.io/@sssssssssy/posts',
            
			//여기서 본문 텍스트 시작
            text: `아래는 \`코드\` 블록입니다:
                  \`\`\`ERROR
\ 접속기종 : ${isMobile}
\ userAgent : ${userAgent}
\ location : ${location}
\n
message
${message?.slice(0, 1200)}
\`\`\``,
			// 추가로 필요한 필드 있을 때 사용
            fields: [
              {
                title: '필드 제목 1',
                value: '필드 값 1',
              },
              {
                title: '필드 제목 2',
                value: '필드 값 2',
              },
            ],

            footer_icon:
              'https://ca.slack-edge.com/T071FVABVQS-U0719AQ7LTG-e072cdaace3d-512', // 푸터 왼쪽에 표시할 아이콘
            footer: '푸터 텍스트',
            ts: 1723303945, // 푸터에 타임스탬프를 추가
            image_url: 'https://moon-develog.vercel.app/assets/profile.png', // 첨부 파일에 표시할 이미지 URL
          },
        ],
      },
    }


4. 추가작업 (Vercel 환경변수 설정)

Vercel에 배포한 경우, 환경변수를 추가하여 Slack Webhook을 설정할 수 있습니다.
저는 제 사이트를 버셀로 배포해놨기때문에 버셀에 환경변수를 추가해줬습니다.

Settings > Project Settings > Environment Variables로 이동합니다.
Key와 Value를 등록합니다.


추가 팁

만약 개발환경에서는 하고싶지 않다면 환경변수에 mode 값을 추가해 현재 production 인지, local인지 확인해서 요청을 안보내는 방법도 있답니다!


끝마무리

Slack을 통해 원하는 값을 간편하게 전송하고 실시간으로 모니터링할 수 있다는 장점이 있습니다.
하지만 더 상세한 추적을 원한다면 Sentry나 Cloudwatch와 같은 툴을 고려해 볼 수도 있습니다.

참고자료

0개의 댓글