- Published on
언어별 디버깅 실전 가이드: Python · JavaScript/TypeScript · Go · Java에서 브레이크포인트, 실행, 프로파일링까지
- Authors
- Name
왜 언어별 디버깅 전략이 달라야 할까
디버깅의 본질은 같지만(재현 → 관찰 → 가설 → 검증), 언어마다 런타임/메모리 모델/툴 체인이 달라서 접근이 달라진다.
- 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개씩 검증: 한 번에 하나만 바꿔보기
- 프로파일링 우선순위: CPU → I/O → 메모리
- 사후 문서화: 원인/탐지 신호/재발 방지 기록
팀 운영 체크리스트
- PR 템플릿에 “재현 방법” 필드 추가
- 장애 리포트에 flamegraph/JFR 첨부
- 디버깅 세션에서 시간 제한(예: 30분) 후 접근 전환
- 로컬/스테이징 디버그 설정 템플릿 공유
마무리
좋은 디버거는 “툴을 많이 아는 사람”이 아니라 문제를 빠르게 좁히는 사람이다. 언어별 도구는 달라도, 핵심은 같다:
관찰 가능한 상태를 만들고, 근거 기반으로 한 단계씩 좁혀라.