Node.js 개발 워크플로우와 디버깅 루틴
npm scripts, nodemon, 디버거 활용으로 개발 속도와 안정성을 높이는 방법
Posted
By okorion
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는 목적이 끝나면 제거한다
- 요청 단위 로그 식별자가 있다
- 에러 스택을 숨기지 않는다
- “왜 안 되는지”를 코드로 설명할 수 있다
