Node.js 애플리케이션을 개발하면서 나는 그동안 console.log
를 써서 디버깅을 하곤 했다. 서버는 보통 debug 같은 패키지를 써서 로그를 수집하곤 하는데, 내가 평소에 작성하는 로그는 이런 로그가 아닌 디버깅 과정에서 임시로 생성하는 로그이다. 물론 회사의 프로젝트에서는 tslint의 규칙^no-console을 통해 테스트 단계에서 걸러내고 있지만, 이러한 lint 규칙을 우회할 수 있는 주석기능이 있기 때문에 까딱 잘못하면 (코드리뷰 없이 긴급 배포된다던가) 프로덕션에도 나갈 여지가 남아있다.
출처: https://blog.risingstack.com/node-js-developer-survey-results-2016/
RisingStack에서 조사한 결과에서도 Node.js개발자들의 디버깅에는 console.log
가 80%로 압도적으로 많이 쓰인다.
그동안 나의 디버깅하는 과정을 살펴보면 다음과 같았다.
- 버그가 의심되는 곳에
console.log
를 추가하고 저장한다. 필요하다면 조건문을 삽입한다. - 변경된 소스코드가 새로 빌드된다.
- 웹 브라우저가 hot-reload 혹은 서버가 재시작
- 상황을 재연하고 로그를 확인한다
- 문제가 해결되었다면 로그를 지우고, 그렇지 않으면 1로 다시 돌아간다.
이게 가벼운 애플리케이션일 때에는 큰 문제가 되지 않지만 리로딩할 때에도 매번 빌드가 일어나는 것이기 때문에 사실상 로직에 큰 변화가 없음에도 불구하고 오랜 시간을 빌드하는 경우[^long-time]가 있다. 어느 순간 이런 소모적인 시간이 너무 아깝다고 생각이 들었고, 간단한 디버깅은 breakpoint와 watch값으로 디버깅을 하는 습관을 들이기로 결심했다.
Conditional Breakpoint
breakpoint는 코드를 실행하는 도중에 멈출 수 있는 기능이다. JavaScript가 아닌 TypeScript같은 언어로 개발하고 있다면 source map을 통해서 breakpoint를 사용할 수 있다. 이 source map을 기준으로 breakpoint를 설정하면 실제 transpile된 코드에서 breakpoint가 작동한다.
breakpoint를 사용할 때 답답한 점은 추상클래스나 상위클래스에 breakpoint를 거는 경우이다. console.log
를 사용하는 디버깅에서는 조건문으로 분기를 해서 특정 상황에서만 로그를 출력하는 식으로 디버깅했겠지만, breakpoint에도 그런 기능이 있을 것 같아 찾아보니 다음과 같이 conditional breakpoint 라는 기능이 있다.
이 기능을 활용하면 굳이 여러번 새롭게 빌드하지 않아도 다양한 경우에 대해 breakpoint를 걸어 볼 수 있다.
정리하자면 다음과 같이 작업 방식이 바뀐 셈이다. 물론 이 과정에서 디버깅때문에 새롭게 빌드되는 시간은 사라졌다.
- 버그가 의심되는 곳에 breakpoint를 생성한다. 필요하다면 conditional breakpoint를 생성한다.
- 이곳에서 관찰할 값(예전에 로그로 출력하던 값)을 watch에 등록한다.
- 새로고침 혹은 재실행
- 상황을 재연하고 watch값을 확인한다.
- 문제가 해결되었다면 breakpoint를 off하고, 그렇지 않으면 1로 다시 돌아간다.
ndb
VSCode를 이용해서 Node.js서버를 Chrome 개발자도구로 디버깅하거나 node의 --inspect
플래그로 디버깅하는 방식[^node-inspect]은 번거로워서 잘 쓰지 않았었는데, ndb는 별다른 설정없이 설치와 실행만으로도 잘 쓸 수 있었다. ndb는 개인적으로 쓰고있기 때문에 일을 할 때는 $ ndb npm run start
처럼 통째로 npm script를 실행하고 있다. ndb가 실행되면 headless chrome을 통해 앞서 다룬 것과 마찬가지로 Chrome 개발자도구를 활용해 디버깅할 수 있다.
[^long-time]: 프로젝트가 mono-repo로 변경된 이후로 공통된 라이브러리를 수정하면 빌드가 3분씩이나 걸리는 경우도 있었다. [^node-inspect]: --inspect
씀