티스토리 뷰

  • 아아.. 블로그 포스팅을 할 건 많은거 같은데 회사 일이 바쁘다 보니 자꾸만 밀린다 ㅜㅜ..
  • 이건 API 서버 개발하다가 정말 유용하게 쓰고 있는 API 서버의 에러를 텔레그램 봇으로 받는 구현체 예제입니다.
  • API 서버의 환경은 다음과 같습니다
    • Node.js 7.x
    • Express.js
    • ECMA2016
  • 텔레그램 봇이 급하신 분들은 errorHandler.js 만 보세요.
  • 테스트로 사용한 전체 소스코드는 Gist 를 참고 바랍니다.

선 스크린샷, 후 코드

서버 로그를 계속 쳐다보고 있을 수가 없으니 에러 발생시 Notifiable 하게 하면 편하다.

구현체

Server.js

import express from 'express';
import occurError from './occurError';
import errorHandler from './errorHandler'
 
const app = express();
 
// 높은 Unit Testability 를 가져가기 위해 Express Route 내에서 특별한 로직을 처리하지 않고 실제 로직은 occurError() 와 같이 모듈로 빼서 리턴 값만 사용합니다.
app.get('/error/:number', (request, response) => {
  try {
    const result = occurError(request.params.number); // occurError.js
    response.json(result);
  } catch (error) {
    // try block 내에서 throw 가 발생했을 때 무조건 errorHandler() 에게 던집니다.
    // request 객체가 필요한 이유는 텔레그램 봇에서 Generating Log Message를 하기 위함입니다.
    // response 객체를 던지는 이유는 실제 response.send() 가 errorHandler 내부에서 일어나기 때문입니다.
    errorHandler(request, response, error); // errorHandler.js
  }
});
 
app.listen(3000, () => {
  console.log('Web Server Listen 3000 port..!');
});

occurError.js

import HttpError from './HttpError';
 
export default function occurError(numberParameter) {
  // numberParameter가 Number로 parseInt 되지 않을 때 에러를 발생시켰습니다.
  if (!parseInt(numberParameter, 10)) {
    throw new HttpError(403, 'PARAMETER_TYPE_EXCEPTION', 'numberParameter is require Number'); // HttpError.js
  }
  return 'This is Good Statement';
};

HttpError.js

// Error Class를 상속받는 HttpError Class를 정의합니다. HTTP 통신에 필요한 Status Code와 추가적인 데이터를 전달하기 위함입니다.
export default class HttpError extends Error {
  constructor(status, code, message) {
    super();
    // 저는 가끔 귀찮을 때 throw new HttpError(null, null, 'Hmm..') 과 같이 사용할 때가 있습니다. 이를 위한 OR 연산입니다.
    this.status = status || 500;
    this.code = code || 'UNEXPECTED_ERROR';
    this.message = message || 'What the Unexpected Error?!?!';
  }
}

errorHandler.js

import fetch from 'node-fetch';
 
const TOKEN = '11111:TelegramBotToken';
// 텔레그램 @BotFather 로 생성한 봇의 TOKEN이 필요합니다.
 
const TELEGRAM_BOT_URL = `https://api.telegram.org/bot${TOKEN}/sendMessage`;
// 텔레그램 봇으로 sendMessage 기능을 수행할 수 있는 URL 입니다.
// 텔레그램 봇 API에 대한 자세한 내용은 https://core.telegram.org/bots/api 을 참고하세요.
// npm에 node-telegram-bot-api 와 같이 추상화된 모듈도 있습니다.
// https://www.npmjs.com/package/node-telegram-bot-api
 
const CHAT_ID = 123456789;
// 메시지를 보낼 채팅창의 chat_id 입니다. 채팅창의 chat_id 를 얻는 방법은 여기서 언급하지 않겠습니다.
// 저는 몇 대의 봇과 다수의 chat_id 을 기능별로 DB로 관리하고 있습니다.
// TOKEN, CHAT_ID 와 같은 상수를 process.env.NODE_ENV 별로 관리하고 싶다면 getconfig 와 같은 것을 사용하면 좋습니다.
 
function errorHandler(request, response, error) {
  // HttpError Class 에서 정의한 status, code, message을 errorHandler에서 다시 정의하는 이유는 HttpError 객체로 Customize하게 잡히지 않는 Exception Error들 까지 포괄하여 처리할 수 있게하기 위함입니다. (ex. Syntax Error)
  const status = error.status || 500;
  const code = error.code || 'UNEXPECTED_ERROR';
  const message = error.message || error.toString();
 
  // 특정 Http Status Code 에서만 텔레그램 봇 활성화
  if ([500, 403, 401].indexOf(status) >= 0) {
    const text = `Occur ${code} Error..!\n\n- IP: ${request.ip}\n- URI: ${request.method} ${request.originalUrl}\n- HEADERS: ${JSON.stringify(request.headers)}\n- BODY: ${JSON.stringify(request.body)}\n\n- STATUS: ${status}\n- CODE: ${code}\n- STACK: ${error.stack}`;
 
    // TELEGRAM_BOT_URL 로 fetch 하는 부분은 동기로 돌아갈 필요가 없습니다. 성공이건 실패건 response.send() 는 실행되어야 하며, 연관되지 않습니다.
    fetch(TELEGRAM_BOT_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        chat_id: CHAT_ID,
        text
      }),
    }).then(result => result.json())
    .catch(telegramError => console.error(telegramError));
  }
 
  response.status(status).send({ code, message });
}


저작자 표시 비영리 변경 금지
신고
프로필사진

Yowu (Yu Yongwoo)

My MBTI type is ENTP. (Of course I do not believe it 100%, but I want to do that) I use Node.js to develop the backend. I use Ubuntu Linux as my development environment, and I love Vim. I am interested in open source and are keen to contribute. I have a bachelor's degree in computer science from Catholic University and now a software engineer at Plating Inc., I spent about 5 years developing and learning, and I am still interested in software development and culture. Recently, I am interested in React, Serverless structure, Domain Design Driven. Sometimes I play drums in the band.