Skip to content
Published on

언어별 디버깅 실전 가이드: Python · JavaScript/TypeScript · Go · Java에서 브레이크포인트, 실행, 프로파일링까지

Authors
  • Name
    Twitter

왜 언어별 디버깅 전략이 달라야 할까

디버깅의 본질은 같지만(재현 → 관찰 → 가설 → 검증), 언어마다 런타임/메모리 모델/툴 체인이 달라서 접근이 달라진다.

  • Python: 동적 타입 + 인터프리터 중심
  • JavaScript/TypeScript: 이벤트 루프/비동기 콜스택
  • Go: 고루틴/채널/간결한 런타임
  • Java: JVM 기반, 프로파일링 생태계 성숙

아래는 실무에서 바로 쓰는 방식만 압축해서 정리했다.


1) Python 디버깅

실행 방법

# 기본 실행
python app.py

# pdb로 즉시 실행
python -m pdb app.py

# pytest에서 실패 지점 진입
pytest -x --pdb

브레이크포인트

def calculate_total(items):
    subtotal = sum(i.price for i in items)
    breakpoint()  # Python 3.7+
    return subtotal

실무 팁:

  • 루프 안 브레이크포인트는 조건부로 제한
  • 데이터 크기 큰 객체는 len()/핵심 필드만 확인

프로파일링

CPU

python -m cProfile -o out.prof app.py
python -m pstats out.prof
# 운영 환경 친화적 샘플링
py-spy top --pid <PID>
py-spy record -o profile.svg --pid <PID>

메모리

pip install memory-profiler
python -m memory_profiler app.py

2) JavaScript/TypeScript (Node.js) 디버깅

실행 방법

# 디버그 포트 오픈
node --inspect src/index.js

# 첫 줄에서 멈춤
node --inspect-brk src/index.js

TypeScript:

# source map 활성화 상태 전제
node --inspect-brk dist/index.js

브레이크포인트

async function syncUser(id: string) {
  const user = await repo.findById(id)
  debugger // DevTools/IDE 연결 시 중단
  return user
}

비동기 디버깅 팁:

  • Promise 체인보다 async/await로 정리
  • 로그에 요청 상관관계 ID(request id) 포함
  • 이벤트 루프 블로킹 의심 시 flamegraph 먼저 확인

프로파일링

# CPU profile 파일 생성
node --cpu-prof dist/index.js

# Heap snapshot
node --heapsnapshot-signal=SIGUSR2 dist/index.js
kill -USR2 <PID>

또는 Chrome DevTools에서:

  • Performance 탭: CPU 병목
  • Memory 탭: 누수 추적

3) Go 디버깅

실행 방법

go run ./cmd/api

Delve 디버거:

dlv debug ./cmd/api
dlv test ./...

브레이크포인트

(dlv) break main.main
(dlv) break service/user.go:42
(dlv) continue
(dlv) print userID

고루틴 이슈 팁:

  • race detector 우선 실행
  • 데드락/고루틴 누수는 stack dump와 pprof 같이 확인
go test -race ./...

프로파일링 (pprof)

import _ "net/http/pprof"
import "net/http"

go func() {
  _ = http.ListenAndServe(":6060", nil)
}()
# CPU 30초
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# Heap
go tool pprof http://localhost:6060/debug/pprof/heap

4) Java 디버깅

실행 방법

./gradlew bootRun
# 또는
java -jar app.jar

원격 디버그 포트:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar

브레이크포인트

  • 예외 브레이크포인트(NullPointerException 등) 적극 사용
  • 조건부 브레이크포인트로 대량 트래픽 중 특정 케이스만 포착
  • logpoint로 서비스 중단 없이 값 관찰

프로파일링

JFR (Java Flight Recorder)

jcmd <PID> JFR.start name=onprod settings=profile duration=120s filename=app.jfr
jcmd <PID> JFR.stop name=onprod

async-profiler

./profiler.sh -d 30 -f cpu.svg <PID>
./profiler.sh -e alloc -d 30 -f alloc.svg <PID>

공통 디버깅 루틴 (언어 무관)

  1. 재현 조건 고정: 입력/버전/환경 변수 캡처
  2. 관측 지점 최소화: 브레이크포인트 남발 금지
  3. 가설 1개씩 검증: 한 번에 하나만 바꿔보기
  4. 프로파일링 우선순위: CPU → I/O → 메모리
  5. 사후 문서화: 원인/탐지 신호/재발 방지 기록

팀 운영 체크리스트

  • PR 템플릿에 “재현 방법” 필드 추가
  • 장애 리포트에 flamegraph/JFR 첨부
  • 디버깅 세션에서 시간 제한(예: 30분) 후 접근 전환
  • 로컬/스테이징 디버그 설정 템플릿 공유

마무리

좋은 디버거는 “툴을 많이 아는 사람”이 아니라 문제를 빠르게 좁히는 사람이다. 언어별 도구는 달라도, 핵심은 같다:

관찰 가능한 상태를 만들고, 근거 기반으로 한 단계씩 좁혀라.