Post

Node.js 개발 워크플로우와 디버깅 루틴

npm scripts, nodemon, 디버거 활용으로 개발 속도와 안정성을 높이는 방법

Node.js 개발 워크플로우와 디버깅 루틴

결론 요약

  • Node.js 개발 생산성은 코드 실력보다 “워크플로우 + 디버깅 루틴”에서 갈린다.
  • 오류는 Syntax / Runtime / Logic으로 나눠야 빠르게 잡힌다.
  • npm scripts는 실행 명령이 아니라 개발 단계의 계약서다.
  • nodemon은 편의 도구가 아니라 피드백 루프를 단축하는 장치다.
  • 디버깅은 console.log브레이크포인트 기반 디버거를 목적별로 분리해야 한다.

Node.js 개발 워크플로우 & 디버깅

실수는 줄이고, 반복 속도는 올리는 실무 루틴


1️⃣ 오류를 3가지로 분류해야 하는 이유

결론

오류를 한 덩어리로 다루면 원인 추적 속도가 기하급수적으로 느려진다. Node.js에서는 반드시 오류를 아래 3가지로 분리한다.

구분언제 발생대표 증상재현 방법잡는 순서주요 도구
Syntax Error실행 전서버가 아예 안 켜짐코드 저장 후 즉시코드 구조부터에디터, ESLint
Runtime Error실행 중요청 시 서버 크래시특정 요청스택 트레이스 → 입력try/catch, debugger
Logic Error실행 후결과가 이상함정상 응답인데 값이 틀림가정 검증debugger, 테스트

왜 필요한가

  • Syntax는 “컴파일 이전 문제”
  • Runtime은 “입력/환경 문제”
  • Logic은 “생각이 틀린 문제”

→ 접근 방식이 완전히 다르다. 한 방식으로 다 잡으려 하면 시간 낭비다.


2️⃣ npm scripts: 명령어가 아니라 개발 단계 설계다

결론

npm scripts는 “편하게 실행하기” 용도가 아니다. 개발 생명주기(dev → test → build → start)를 강제하는 장치다.

최소 권장 구조

1
2
3
4
5
6
7
8
{
  "scripts": {
    "dev": "nodemon app.js",
    "test": "jest",
    "build": "tsc",
    "start": "node dist/app.js"
  }
}

왜 dev / start를 나누는가

  • dev: 빠른 재시작, 로그 많음, 실패 허용
  • start: 안정성, 예외 최소화, 프로덕션 기준

이 분리가 없으면 로컬에서는 되는데 배포에서 터지는 코드가 양산된다.

환경변수(cross-env) – 실무 기준만

1
2
"dev": "cross-env NODE_ENV=development nodemon app.js",
"start": "cross-env NODE_ENV=production node app.js"
  • 코드 분기 기준: process.env.NODE_ENV
  • 목적: 로깅 수준, 에러 메시지, 캐시 정책 분리

3️⃣ nodemon을 “왜” 쓰는가

결론

nodemon의 가치는 자동 재시작이 아니라 수정 → 실행 → 확인 사이의 인지 비용 제거다.

nodemon이 만드는 변화

  • 서버 재시작을 “의식하지 않게 됨”
  • 작은 수정도 즉시 검증 → 실험 빈도 증가
  • 결과적으로 Logic Error를 초기에 잡음

주의점

  • 프로덕션에서는 절대 사용하지 않는다
  • 무한 재시작은 Runtime Error 신호다 (에러 삼킴 금지)

4️⃣ Node.js 디버깅 2가지 방식 (목적이 다르다)

① console 기반 디버깅

  • 장점: 빠르다, 즉각적이다
  • 단점: 흐름 파악이 어렵다, 제거를 잊기 쉽다
1
console.log('req.body:', req.body);

👉 입력 확인용으로만 사용


② debugger(브레이크포인트) 기반 디버깅

  • 장점: 상태 추적, 가정 검증에 최적
  • 단점: 설정을 모르면 진입 장벽이 있음

VSCode 기준 개념

  • launch: Node 프로세스를 VSCode가 직접 실행
  • attach: 이미 떠 있는 Node 프로세스에 연결
1
2
3
4
5
{
  "type": "node",
  "request": "launch",
  "program": "${workspaceFolder}/app.js"
}

👉 Logic Error는 반드시 debugger로 잡는다


5️⃣ 실무에서 바로 쓰는 로그 전략 (request id)

결론

로그는 “출력”이 아니라 요청 단위 추적 도구다.

간단한 request id 아이디어

1
2
3
4
5
app.use((req, res, next) => {
  req.requestId = Date.now().toString(36);
  console.log(`[${req.requestId}] ${req.method} ${req.url}`);
  next();
});
  • 하나의 요청 흐름을 로그로 묶을 수 있음
  • 장애 분석 시 체감 속도 차이가 큼

6️⃣ 문제–원인–해결 사례 3개

사례 1: 서버가 켜지지 않음

  • 문제: Unexpected token
  • 원인: Syntax Error (괄호/쉼표)
  • 해결: 실행 전 에디터/포맷터로 구조 확인

사례 2: 특정 요청에서만 서버 종료

  • 문제: Cannot read property 'id' of undefined
  • 원인: Runtime Error (입력 검증 없음)
  • 해결: early return + 입력 검증 추가
1
2
3
4
5
// 나쁜 예
const id = req.body.user.id;

// 개선 예
if (!req.body.user) return res.status(400).end();

사례 3: 응답은 오는데 데이터가 이상함

  • 문제: 합계가 계속 0
  • 원인: Logic Error (가정 오류)
  • 해결: debugger로 변수 흐름 추적

내 로컬 셋업 체크리스트 (10개)

  • dev / start npm scripts가 분리되어 있다
  • nodemon은 dev에서만 사용한다
  • NODE_ENV 기준 분기가 있다
  • Syntax Error는 실행 전에 잡는다
  • Runtime Error는 재현 시나리오를 만든다
  • Logic Error는 debugger로 추적한다
  • console.log는 목적이 끝나면 제거한다
  • 요청 단위 로그 식별자가 있다
  • 에러 스택을 숨기지 않는다
  • “왜 안 되는지”를 코드로 설명할 수 있다

This post is licensed under CC BY 4.0 by the author.