[Node.js코드랩] 5.커스텀 모듈 Debug

🌳목표

노드에서 꽤 많이 사용되는 debug 모듈을 직접 만들어 봅니다.

Debug를 사용하는 이유

Debug 모듈은 노드에서 가장 많이 사용하는 디버깅 모듈입니다. 로그를 구조적으로 기록할 수 있다는 점에서 console.log 보다 뛰어납니다. Debug가 갖고 있는 장점은 아래 두 가지입니다.

  • 태그를 지정한 로그 함수를 만들 수 있다
  • 태그별로 색상을 줘서 로그 식별이 수월하다

사용 방법은 아래처럼 간단합니다.

const debug = require("debug")("my_tag")
debug("my_log") // "my_tag my_log"

이것을 직접 만들어 보겠습니다.

테스트 코드

테스트를 코드가 있는 브랜치로 이동하겠습니다.

$ git checkout -f module/debug-spec

utils/debug.spec.js에 있는 테스트 코드를 살펴 보지요. 네 부분으로 나누어 설명하겠습니다.

require('should');
const sinon = require('sinon');
const debug = require('./debug');

describe('debug', () => {
  describe('생성', () => {
    it('태그명을 인자로 받는다 (없으면 예외를 던진다)', () => {
      should(() => debug()).throw();
    })

필요한 모듈을 가져와서 상수에 할당했습니다. debug 모듈의 생성 시점의 기능을 테스트하는 코드인데 태그명을 인자로 받도록 합니다. 만약 태그 인자가 없으면 예외를 던지도록 했죠.

    it('함수를 반환한다', () => {
      const debug = require('./debug')('mytag');
      should(typeof debug).be.equal('function');
    })
  })

태그를 전달한 함수는 다시 함수를 반환하는지 typeof로 체크했습니다.

  describe('반환된 함수', () => {
    let debug, tag, msg;

    beforeEach(() => {
      tag = 'mytag';
      debug = require('./debug')(tag);
      msg = 'my log message';
    })

    it('tag + message 형식의 로그 문자열을 반환한다', () => {
      const expected = `${tag} ${msg}`;
      const actual = debug(msg);
      actual.should.be.equal(expected);
    })

이것은 반환된 함수를 테스트하는 코드입니다.

모카 프레임웍에서 제공하는 beforeEach() 함수는 테스트 케이스(it) 실행 전마다 동작합니다. 매 테스트케이스에서 debug 객체를 만들기 때문에 중복 코드를 이곳에 모아논 것이죠. DRY함은 테스트 코드에서도 예외일 수 없습니다.

반환한 함수에 메세지(msg)를 전달해 실행하면 tag + msg 문자열이 반환되는지 점검하는 코드입니다.

  it('로그 문자열을 인자로 console.log 함수를 실행한다', () => {
    sinon.spy(console, 'log');
    const expected = `${tag} ${msg}`;

    debug(msg);

    sinon.assert.calledWith(console.log, expected);
  })
})

sinon 라이브러리를 이용해서 console.log 메소드에 스파이를 심었습니다. 그리고 dubug(msg)를 실행했을 때 기대하는 문자열이 console.log 함수 인자로 전달되는지 점검하는 코드입니다.

🐤실습 - debug 모듈을 만들어 봅니다

위 테스트 코드에서 요구하는 debug 모듈을 만들어 보세요.

🐤풀이

잘 풀어 보셨나요? utils/debug.js 파일을 새로 만들어 같이 확인해 보겠습니다.

const debug = tag => {
  if (!tag) throw Error("tag should be required")
}

module.exports = debug

먼저 debug를 tag 인자를 받는 함수로 정의 했습니다. 인자가 없으면 바로 에러를 던지도록 해서 첫 번째 테스트 케이스를 통과하도록 했구요. 마지막엔 역시 모듈로 노출하였습니다.

const debug = tag => {
  // ...
  return msg => {
    const logString = `${tag} ${msg}`;
    console.log(logString);
    return logString;
  }
// ...

debug 함수는 다시 함수를 반환하도록 했죠. 그래서 msg를 인자로 받는 함수를 정의했습니다. 그런 뒤 클로져로 캡쳐된 tag 값과 msg를 조합해서 로그 문자열(logString)을 만듭니다. 이 값은 console.log와 반환 값으로 사용하였습니다.

그럼 테스트를 돌려 볼까요?

$ npm test

debug
    생성
      ✓ 태그명을 인자로 받는다 (없으면 예외를 던진다)
      ✓ 함수를 반환한다
    반환된 함수
      ✓ tag + message 형식의 로그 문자열을 반환한다
      ✓ 로그 문자열을 인자로 console.log 함수를 실행한다
    메세지 출력함수
      ✓ _log() 함수 결과문자열을 console.log()함수의 인자로 전달하여 실행한다

모든 테스트 케이스에 통과했습니다.

🐤실습-색상도 추가해 보세요

태크 문자 색상을 랜덤으로 출력하도록 debug 모듈을 개선해 보세요.

힌트: 스택오버플로우 How to change node.js's console font color

🐤풀이

힌트가 도움이 되었나요? 그럼 uitls/debug.js를 같이 개선해 보겠습니다.

문자에 색상을 추가하려면 다음과 같은 형식이어야 합니다.

문자코드 + 문자 + 리셋코드

따라서 우리 코드 상단에 아래와 같이 색상 정보를 추가합니다.

const colors = [
  { name: "cyan", value: "\x1b[36m" },
  { name: "yellow", value: "\x1b[33m" },
  { name: "red", value: "\x1b[31m" },
  { name: "green", value: "\x1b[32m" },
  { name: "magenta", value: "\x1b[35m" },
]
const resetColor = "\x1b[0m"

힌트에서 언급한 것 처럼 색상 코드를 딕셔너리로 만들었습니다. 그리고 resetColor도 따로 상수로 두었구요.

const debug = tag => {
  const randIdx = Math.floor(Math.random() * colors.length) % colors.length
  const color = colors[randIdx]

태그 인자를 받자마자 태그의 색상을 정하기 위해 colors 배열에서 랜덤으로 인덱스를 구하는 코드 입니다. 마지막 color 상수에는 name, value를 가지는 컬러 객체가 랜덤으로 저장 되겠지요.

return msg => {
  const logString = `${color.value}[${tag}]${resetColor} ${msg}`
  console.log(logString)
  return logString
}

반환된 함수에서 이 랜덤 색상 객체를 사용합니다. logString을 만들때 태그 문자열만 색상을 지정했습니다.

Debug 활용

우리 어플리케이션 코드에 이 debug 모듈을 이용해서 로그를 남겨 봅시다.

// app.js
const debug = require("../utils/debug")("app")
// ...

debug("app is initiated")
// bin.js
const debug = require('./utils/debug')('bin')
// ...

app.listen(port, hostname, () => {
  debug(`Server running at http://${hostname}:${port}/`);
// src/Application.js
const debug = require("../utils/debug")("Application")
// ...

const listen = (port = 3000, hostname = "127.0.0.1", fn) => {
  _server.listen(port, hostname, fn)
  debug("server is listening")
}

서버를 실행하면 다음과 같이 예쁜 모양의 로그가 색상 별로 나오는 걸 확인할수 있습니다.

$ npm start

Debug 모듈은 아래 써드 파티 라이브러리 부분입니다.

정리

  • 커스텀 모듈인 debug를 만들었습니다.
  • 태그와 색상으로 로깅 기능을 개선하였습니다.

목차 바로가기