필사 모드: 소프트웨어 디버깅 마스터리 2026 완벽 가이드 - GDB · LLDB · rr · Pernosco · pdb · Chrome DevTools · Bun inspect · DAP 심층 분석
한국어> "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian Kernighan
소프트웨어 엔지니어가 평생 가장 많이 하는 일은 코드를 작성하는 것이 아니라 **버그를 디버깅(Debugging)** 하는 것입니다. 다양한 통계가 그 비중을 25%에서 50% 사이로 잡지만, 시니어가 될수록 이 비율은 오히려 늘어나는 경향이 있습니다 — 복잡한 분산 시스템, 잘 안 보이는 메모리 손상, 비결정적 동시성 버그, 운영 환경에서만 재현되는 미스터리. 2026년 현재 디버깅은 단순히 `print` 문을 찍는 일이 아니라, **과학적 방법론 + 거대한 도구 생태계 + AI 어시스트**를 결합한 하나의 학문이 되어 가고 있습니다.
이 글에서는 GDB 15, LLDB 19, rr, Pernosco, WinDbg TTD 같은 네이티브 디버거부터 Chrome DevTools 2025의 AI 어시스트, Bun/Deno/Node의 inspector, Python pdb/pdbpp/ipdb/pudb 비교, Rust·Go·Java의 언어별 디버거, eBPF 기반 관측, Valgrind·Sanitizers, DAP(Debug Adapter Protocol) 표준까지 — 그리고 가장 중요한 **디버깅 마인드셋**을 한 번에 정리합니다.
1. 디버깅 마인드셋 — 가설 기반(Hypothesis-Driven) 과학적 디버깅
훌륭한 디버깅은 도구가 아니라 **사고방식(Mindset)** 에서 시작합니다. Andreas Zeller의 명저 *Why Programs Fail*(2009/2023 2판)은 디버깅을 과학자가 가설을 세우고 실험으로 반증해 가는 과정에 비유합니다. 핵심 루프는 다음과 같습니다.
1. **현상(Symptom) 관찰**: 어떤 입력에서, 어떤 환경에서, 어떤 결과가 나왔는가
2. **가설(Hypothesis) 수립**: 가능한 원인 후보 N개 — 가장 가능성 높은 순으로 정렬
3. **예측(Prediction)**: 이 가설이 참이라면, A를 바꾸면 B가 바뀔 것이다
4. **실험(Experiment)**: 최소한의 변경으로 예측을 검증
5. **반증(Falsification)** 또는 **세분화(Refinement)**: 결과에 따라 가설을 버리거나 좁히기
이 사이클을 빠르게 돌리는 사람이 좋은 디버거입니다. 초보가 가장 자주 빠지는 함정은 **무작위로 코드를 바꾸면서 잘 되기를 비는 것** — Zeller는 이것을 "shotgun debugging"이라 부르며, 시간만 낭비하고 새로운 버그를 만들기 일쑤라고 지적합니다. 또 다른 함정은 **확증 편향(Confirmation Bias)**: 자신의 가설을 뒷받침하는 증거만 찾고, 반박하는 증거를 무시하는 것. 좋은 디버거는 자기 가설을 적극적으로 **반증하려고** 노력합니다.
2026년 시니어 개발자들이 강조하는 또 다른 원칙은 **"버그는 정보다(A bug is information)"** 입니다. 버그가 발생했을 때 가장 비싼 자원은 그 버그를 재현하는 정확한 조건이며, 이를 잃기 전에 **재현 가능한 최소 케이스(Minimal Reproducer)** 를 확보하는 것이 디버깅의 절반입니다. Delta debugging 알고리즘(Zeller, 2002)은 이 과정을 자동화하며, modern test 프레임워크의 shrinking(Hypothesis, QuickCheck, fast-check)도 같은 아이디어입니다.
2. GDB 15 — 모던 네이티브 디버거의 표준
GDB(GNU Debugger, `gnu.org/software/gdb`)는 1986년 Richard Stallman이 시작한 이래 40년간 진화해 온 사실상 표준 네이티브 디버거입니다. 2025년 1월 출시된 **GDB 15**는 Python 3.12 통합, DWARF 5 완전 지원, 그리고 Rust/Swift/Modula-2 같은 후발 언어에 대한 개선된 pretty-printing을 가져왔습니다.
기본 워크플로는 다음과 같습니다.
소스에서 빌드 — 반드시 -g (디버그 심볼), -O0 또는 -Og (최적화 억제)
gcc -g -Og main.c -o app
디버거 진입
gdb ./app
또는 실행 중인 프로세스에 attach
gdb -p 12345
또는 core dump 분석
gdb ./app core.12345
핵심 명령어는 `break`, `run`, `next`, `step`, `print`, `backtrace`(축약 `bt`), `info`, `watch`, `continue`입니다.
(gdb) break main.c:42 # 라인 단위 브레이크포인트
(gdb) break parse_request # 함수명으로
(gdb) break parse_request if errno == 5 # 조건부
(gdb) watch *ptr # 메모리가 바뀌면 멈춤
(gdb) catch throw # C++ 예외 발생 시 멈춤
(gdb) bt full # 전체 스택 + 로컬 변수
(gdb) frame 3 # 3번 프레임으로 이동
(gdb) info locals # 로컬 변수 일람
(gdb) print/x *(struct request *)0x7ffe8000 # 16진수로 구조체 캐스팅 출력
GDB 15에서 가장 강력한 것은 **Python 스크립팅**입니다. `gdb.execute`, `gdb.parse_and_eval`, custom pretty-printer를 통해 컴파일된 도메인 객체를 사람이 읽을 수 있는 형식으로 출력할 수 있습니다.
~/.gdbinit 또는 별도 .py 파일
class RequestPrinter:
def __init__(self, val):
self.val = val
def to_string(self):
path = self.val['path'].string()
method = self.val['method'].string()
return f"Request {method} {path}"
def lookup_request(val):
if str(val.type) == 'struct request':
return RequestPrinter(val)
return None
gdb.pretty_printers.append(lookup_request)
TUI(Text User Interface) 모드 `gdb -tui` 또는 `Ctrl+X A`는 터미널을 4분할해 소스·레지스터·어셈블리·명령어 입력을 동시에 보여줍니다. 2025년에는 `gdb-dashboard`(`github.com/cyrus-and/gdb-dashboard`)가 사실상 표준 UI 확장으로 자리잡았습니다.
3. LLDB 19 — Apple/LLVM 생태계의 표준
LLDB(`lldb.llvm.org`)는 LLVM 프로젝트의 디버거로, 2025년 9월 출시된 **LLVM 19**의 일부로 함께 발전합니다. Apple의 모든 플랫폼(macOS, iOS, watchOS, visionOS)에서 Xcode가 내부적으로 사용하고, Rust의 `rust-lldb`, Swift의 디폴트 디버거이기도 합니다.
명령어는 GDB와 비슷하지만 약간 다른 어휘를 씁니다.
(lldb) breakpoint set --file main.c --line 42 # b main.c:42 단축형도 지원
(lldb) breakpoint set --name parse_request
(lldb) run
(lldb) thread step-over # n
(lldb) thread step-in # s
(lldb) frame variable # info locals
(lldb) frame select 3
(lldb) thread backtrace # bt
(lldb) memory read --size 4 --format x 0x100000 # 메모리 hex dump
(lldb) expression -- (*request).path # p (*request).path
LLDB의 강점은 **Python 스크립트 브리지**가 GDB보다 더 깊이 통합되어 있다는 점입니다. `script` 명령으로 LLDB 안에서 바로 Python REPL이 떠오르고, `SBValue`, `SBFrame`, `SBProcess` 같은 객체로 디버그 세션을 프로그래밍 가능합니다.
(lldb) script
>>> import lldb
>>> frame = lldb.thread.GetSelectedFrame()
>>> req = frame.FindVariable('request')
>>> print(req.GetChildMemberWithName('path').GetSummary())
Swift 디버깅 시 `po`(print object) 명령으로 Swift 객체의 `description`을 호출하며, `v`(variable) 명령은 더 빠른 변수 출력에 최적화되어 있습니다. 2025년 Swift 6.1부터는 Swift Macros 디버깅이 LLDB에 통합되어, 매크로 확장 결과를 한 번에 inspect할 수 있습니다.
Rust에서는 `rust-lldb`(또는 `rust-gdb`) 래퍼가 표준 라이브러리 타입(`Vec`, `String`, `HashMap`)의 pretty-printer를 자동으로 로드합니다.
4. rr — 결정론적 record & replay 디버깅
rr(`rr-project.org`)은 Mozilla가 2014년에 발표한 record-and-replay 디버거로, **비결정적인(non-deterministic) 버그를 결정적으로 재현**할 수 있게 해 줍니다. 한 번 녹화한 실행을 동일한 시스템 콜·스레드 스케줄·시그널 순서로 그대로 다시 재생하므로, 같은 버그가 무한 반복됩니다.
녹화
rr record ./app --input data.json
재생 (gdb 인터페이스로 진입)
rr replay
또는 가장 최근 녹화를 자동 재생
rr replay -p 0
녹화 디렉터리 위치
ls ~/.local/share/rr/
rr의 진짜 매력은 **역방향(Reverse) 실행**입니다.
(rr) reverse-continue # 직전 브레이크포인트까지 뒤로
(rr) reverse-step # 한 단계 뒤로
(rr) reverse-next # 한 줄 뒤로
(rr) watch -l x # x가 어디서 바뀌었는지 거꾸로 추적
전통적인 디버깅 시나리오 "포인터가 NULL인데 어디서 NULL이 되었는지 모르겠다"가 rr에서는 `reverse-continue` + `watch *ptr` 한 줄로 해결됩니다. 메모리가 바뀐 시점까지 거꾸로 실행하므로, 책임자가 누구인지 즉시 보입니다.
제약은 크게 세 가지입니다. Intel CPU에서만 동작(2026년 들어 AMD Zen 5 일부 지원이 추가되었으나 여전히 베타), CPU의 PMC(Performance Monitoring Counter)를 사용하므로 가상화 환경에서 동작이 까다로움, 단일 스레드/다중 스레드 모두 가능하지만 다중 코어 병렬 실행은 시리얼라이즈됨(성능 오버헤드 1.2배~5배). 그래도 비결정적 버그 디버깅의 효과는 압도적이라 Firefox, Chromium, Microsoft Edge 팀이 모두 rr 기반 워크플로를 가집니다.
5. Pernosco — 클라우드 omniscient 디버거
Pernosco(`pernos.co`)는 rr 창시자 Robert O'Callahan과 Kyle Huey가 2018년에 Mozilla에서 spin-off한 클라우드 디버깅 서비스입니다. 핵심 아이디어는 **rr 녹화를 클라우드에 업로드하면, 전체 실행을 미리 분석해 "전지적(omniscient)" 디버그 세션을 웹 UI로 제공**한다는 것입니다.
로컬에서 rr 녹화
rr record ./app
Pernosco CLI로 업로드 (Pernosco 계정 필요)
pernosco-submit upload ~/.local/share/rr/latest-trace
업로드 후 Pernosco는 ~1~10분간 백엔드에서 전체 트레이스를 인덱싱하고, 그 결과로 다음을 제공합니다.
- **임의 시점 점프**: 시간축에서 아무 시점이나 클릭하면 그 시점의 전체 메모리·레지스터·콜스택이 즉시 보임
- **dataflow 추적**: 어떤 값이 어디서 왔는지 거꾸로 추적
- **자동 root-cause 후보 강조**: "이 NULL은 30초 전 line 42에서 free된 뒤 reuse되었습니다"
- **공유 가능한 URL**: 동료에게 디버그 세션 자체를 공유
Mozilla, Cloudflare, Sourcegraph, Discord 같은 회사들이 production-grade 버그 헌팅에 사용합니다. 2026년 들어 Pernosco는 단가가 다소 비싸지만(트레이스당 ~10~50USD), 일주일짜리 미스터리 버그를 한 시간에 해결한다면 압도적으로 이익입니다.
6. WinDbg + TTD — Windows의 시간 여행 디버깅
Windows 진영의 표준 디버거는 **WinDbg**(`learn.microsoft.com/windows-hardware/drivers/debugger/`)이며, 2018년부터 **WinDbg Preview**(현재 WinDbg "엔진 2", 2025년에 모던 UI로 통합)가 메인 클라이언트로 자리잡았습니다. WinDbg는 유저 모드뿐 아니라 **커널 모드**, **크래시 덤프**, 그리고 **TTD(Time Travel Debugging)** 까지 다룹니다.
TTD는 rr/Pernosco의 Windows 버전 격으로, `TTTracer.exe`로 실행을 녹화하면 `.run` 파일이 생성됩니다.
관리자 명령창에서 녹화 시작
TTTracer.exe -out C:\traces\ -launch myapp.exe
WinDbg에서 .run 파일 열기 → 역방향 디버깅 가능
WinDbg 명령창
0:000> g- # 역방향 go
0:000> t- # 역방향 step
0:000> wt- # 역방향 watch
WinDbg의 진가는 **커널 디버깅**입니다. BSOD(Blue Screen of Death) dump 파일(`.dmp`)을 열어 누가 IRQL 위반을 일으켰는지, 어떤 드라이버가 deadlock을 만들었는지 추적할 수 있습니다. Windows 보안팀, 안티치트 엔진, EDR 솔루션 개발자들이 매일 다루는 도구입니다.
7. Chrome DevTools 2025 — AI 어시스트와 MCP
Chrome DevTools는 2008년 첫 등장 이후 가장 많이 진화한 디버거 중 하나로, 2025년 들어 **AI 어시스트(Insights, Recorder)** 와 **DevTools MCP**가 합류하며 본질적인 변화를 맞이했습니다.
핵심 패널:
- **Sources**: 브레이크포인트, 조건부, logpoint, async stack traces, source map 자동 매핑
- **Performance**: 트레이스 녹화 → flame chart → AI 기반 자동 인사이트 ("LCP가 늦은 원인은 main thread blocking입니다")
- **Memory**: Heap snapshot, Allocation timeline, detached DOM 트리, retainer 트리
- **Network**: 요청별 timing waterfall, Initiator 체인, throttling 시뮬레이션
- **Coverage**: 사용되지 않은 JS/CSS 측정
- **Recorder**: 사용자 시나리오 녹화 → 자동 Playwright/Puppeteer 스크립트 생성
2025년 4월 출시된 **Chrome DevTools MCP**(`github.com/ChromeDevTools/chrome-devtools-mcp`)는 Claude·Cursor·Codex 같은 AI 코딩 에이전트가 실제 브라우저를 조작하면서 디버깅·테스트할 수 있게 합니다. 예를 들어 Claude에게 "콘솔 에러가 나는 버튼을 찾아 고쳐줘"라고 요청하면, MCP를 통해 DevTools를 조작해 콘솔을 읽고, 셀렉터를 찾고, 트레이스를 떠서 원인을 분석합니다.
Node.js 디버깅에도 Chrome DevTools가 표준입니다. `node --inspect`로 시작한 프로세스는 `chrome://inspect` 또는 `about:inspect`에서 발견되며, V8 inspector 프로토콜로 연결됩니다.
Node.js: 시작과 동시에 inspector 열기
node --inspect index.js
첫 줄에서 breakpoint를 걸고 attach 대기
node --inspect-brk index.js
포트 지정 (Docker 등에서)
node --inspect=0.0.0.0:9229 index.js
8. Firefox DevTools / Safari Web Inspector
Firefox DevTools(`firefox-source-docs.mozilla.org/devtools-user/`)는 Chrome과 거의 동등한 기능을 제공하면서도 몇 가지 차별점을 가집니다.
- **CSS Grid Inspector**: 그리드 라인·갭·영역을 시각적으로 오버레이 (Chrome도 따라잡았지만 Firefox가 원조)
- **Accessibility Inspector**: WCAG 위반 자동 감지
- **Network Throttling**: WebRTC·WebSocket 트래픽 분석 (Chrome보다 풍부)
- **about:debugging**: 별도 페이지에서 다른 탭·service worker·extension을 한 번에 attach
Safari Web Inspector는 iOS/macOS 디바이스 디버깅에 필수입니다. Mac의 Safari → 개발자 메뉴 → iPhone 시뮬레이터/실기를 선택해 attach합니다. 2025년 Safari 18.4부터 WebGPU 지원 디버거가 정식 출시되었고, 2026년 Safari 19에서는 React DevTools 통합이 네이티브 지원됩니다.
세 브라우저 모두 **WebKit Inspector Protocol**(Safari)·**CDP**(Chrome DevTools Protocol)·**Firefox Remote Debugging Protocol**이 각자 다르지만, Playwright·Puppeteer는 이 차이를 추상화합니다.
9. Python 디버깅 — pdb · pdbpp · ipdb · pudb · debugpy
Python에는 의외로 많은 디버거 옵션이 있습니다. 기본 `pdb`(`docs.python.org/3/library/pdb.html`)부터 시작해 봅시다.
def buggy(x, y):
pdb.set_trace() # 또는 Python 3.7+의 breakpoint() 빌트인
z = x / y
return z
buggy(10, 0)
`breakpoint()` 빌트인은 환경 변수 `PYTHONBREAKPOINT`로 어떤 디버거를 띄울지 바꿀 수 있습니다 (`PYTHONBREAKPOINT=ipdb.set_trace`).
**pdbpp**(`github.com/pdbpp/pdbpp`)는 `pdb`의 드롭인 대체로, syntax highlighting, sticky 모드(현재 함수를 항상 위에 표시), tab completion을 추가합니다. 설치 후 `import pdb; pdb.set_trace()`만으로 동작이 자동 강화됩니다.
**ipdb**(`github.com/gotcha/ipdb`)는 IPython 기반으로, `?`, `??`, magic 명령 같은 IPython 기능을 디버그 세션에서 그대로 쓸 수 있습니다.
**pudb**(`github.com/inducer/pudb`)는 터미널 GUI 디버거로, curses 기반 4분할 화면(소스·스택·변수·명령)을 제공합니다. SSH 세션에서 X11이 없을 때 가장 인기 있는 선택지입니다.
**rpdb**(`github.com/tamentis/rpdb`)는 원격 디버거로, 네트워크 소켓을 통해 다른 머신에서 attach할 수 있습니다. 운영 서버에 일시적으로 들어가야 할 때 유용합니다.
**debugpy**(`github.com/microsoft/debugpy`)는 VS Code의 기본 Python 디버거 백엔드로, DAP 프로토콜을 통해 IDE와 통신합니다.
운영 환경에서 attach 대기
debugpy.listen(("0.0.0.0", 5678))
debugpy.wait_for_client() # 클라이언트 attach까지 블록
비교 정리:
| 도구 | 사용 시점 | 강점 |
|---|---|---|
| pdb | 외부 의존성 없는 빠른 진입 | 빌트인, `breakpoint()` |
| pdbpp | 로컬 단독 사용 | 자동 강화, sticky 모드 |
| ipdb | 데이터/노트북 작업 | IPython 통합 |
| pudb | SSH·터미널 환경 | 풀 화면 GUI |
| rpdb | 운영 서버 attach | 원격 소켓 |
| debugpy | IDE(VS Code) | DAP, 멀티 클라이언트 |
10. Bun 1.3 / Deno 2 / Node.js — JavaScript 런타임 디버깅
**Bun**(`bun.sh`)은 2024년 9월 1.2 출시 이후 **빌트인 인스펙터**가 안정화되었고, 2026년 1월 1.3에서는 Chrome DevTools 외에 VS Code DAP 직접 지원이 추가되었습니다.
Bun: 빌트인 inspector 시작
bun --inspect index.ts
시작과 동시에 첫 줄에서 멈추기
bun --inspect-brk index.ts
명시적 포트
bun --inspect=ws://0.0.0.0:6499 index.ts
Bun inspector는 V8 inspector 프로토콜과 호환되지만 자체 JavaScriptCore 기반이라 일부 트레이스 모양이 다릅니다. 2026년 4월 출시된 Bun 1.3.2는 source map 처리에 큰 개선을 가져왔습니다.
**Deno 2**(`deno.com`, 2024년 10월 1.0 호환 GA)는 Chromium V8 위에 있으므로 Node와 인스펙터가 거의 동일합니다.
deno run --inspect=0.0.0.0:9229 server.ts
deno run --inspect-brk server.ts
**Node.js**는 가장 성숙합니다.
node --inspect index.js # 기본 9229
node --inspect-brk index.js # 첫 줄에서 멈춤
node --inspect-port=9230 index.js # 포트 명시
node --inspect=0.0.0.0:9229 index.js # 외부 attach 허용
`chrome://inspect`에서 Discover targets가 켜져 있으면 자동으로 잡힙니다. VS Code에서는 `launch.json`에 `"type": "node"`만 적으면 됩니다.
VS Code의 `launch.json` 예시 ([참고]):
// launch.json (각 필드는 JSON, 외부 평가 안 함)
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/index.js"
}
]
}
`${workspaceFolder}`는 VS Code가 평가하는 변수이며, 일반 JS 템플릿 리터럴이 아닙니다.
11. Rust 디버깅 — dbg! · cargo-flamegraph · tokio-console
Rust 디버깅의 가장 빠른 도구는 `dbg!` 매크로입니다.
fn calculate(x: i64, y: i64) -> i64 {
let intermediate = dbg!(x * 2); // stderr에 파일·라인·값을 출력
let result = dbg!(intermediate + y);
result
}
출력은 `[src/main.rs:2] x * 2 = 20` 형태로, `println!("{:?}", expr)`보다 훨씬 많은 컨텍스트를 남깁니다.
본격적인 디버거로는 `rust-gdb`(GDB 래퍼) 또는 `rust-lldb`를 씁니다. 두 래퍼 모두 Rust 표준 라이브러리의 pretty-printer를 자동으로 로드하므로 `Vec<i32>`, `HashMap<K, V>` 같은 타입도 사람 읽기 쉽게 표시됩니다.
cargo build --profile=dev # 기본 debug 빌드
rust-gdb target/debug/myapp
rust-lldb target/debug/myapp
**tokio-console**(`github.com/tokio-rs/console`)은 Tokio 런타임을 위한 전용 디버거로, 실행 중인 비동기 태스크·리소스·deadlock을 실시간으로 시각화합니다.
// Cargo.toml에 tokio = { features = ["tracing"] }, console-subscriber 추가
fn main() {
console_subscriber::init();
// ...
}
다른 터미널에서 `tokio-console` 실행 → busy 태스크·polling 빈도·시간이 오래 걸리는 태스크가 즉시 보입니다. Tokio 기반 서버에서 deadlock·starvation 디버깅에 필수입니다.
**cargo-flamegraph**(`github.com/flamegraph-rs/flamegraph`)는 perf/dtrace 위에서 동작하는 wrapper로, 한 줄로 flame graph를 만들어 줍니다.
cargo flamegraph --bin myapp -- --some-arg
flamegraph.svg 생성
**pprof-rs**(`github.com/tikv/pprof-rs`)는 인프로세스 CPU 프로파일러로, 운영 환경에서 Go의 pprof와 동일한 형식으로 결과를 export 할 수 있습니다.
12. Go 디버깅 — Delve(`dlv`)
Go의 표준 디버거는 **Delve**(`github.com/go-delve/delve`)로, GDB도 동작하긴 하지만 goroutine·channel 같은 Go 고유 개념을 잘 이해하지 못해 거의 쓰이지 않습니다. 2025년 들어 Go 1.23·1.24와 함께 Delve 1.24가 출시되었고, generics 디버깅이 안정화되었습니다.
디버그 모드로 실행
dlv debug ./cmd/server
컴파일된 바이너리 실행 (debug 심볼 포함 필수)
dlv exec ./server
실행 중인 프로세스에 attach
dlv attach 12345
core dump 분석
dlv core ./server core.12345
remote 모드 (헤드리스)
dlv --listen=:2345 --headless --api-version=2 debug
Delve 명령어:
(dlv) break main.go:42
(dlv) break main.processRequest
(dlv) condition 1 r.UserID == 42 # 조건부 브레이크포인트
(dlv) continue
(dlv) next
(dlv) step
(dlv) goroutines # 모든 goroutine 보기
(dlv) goroutine 3 # 3번 goroutine 선택
(dlv) bt
(dlv) print req.Body
(dlv) call myFunc(42) # 위험하지만 가능
VS Code의 `Go` 익스텐션이 Delve를 백엔드로 쓰므로 GUI 디버깅도 자연스럽습니다. 2025년부터 GoLand도 Delve를 직접 임베드합니다.
13. Java 디버깅 — JFR + JMC + IntelliJ debugger
Java는 JVM 표준 프로토콜 **JDWP(Java Debug Wire Protocol)** 가 있어, IDE·디버거 사이가 표준화되어 있습니다. **JDB** CLI는 deprecated가 되었고, 현실적으로는 **IntelliJ IDEA**·**Eclipse**·**VS Code Java Pack**이 JDWP를 통해 디버깅합니다.
JVM을 디버그 모드로 시작
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar
suspend=y로 하면 IDE attach까지 멈춤
성능·메모리 디버깅에는 **JFR(Java Flight Recorder)** + **JMC(JDK Mission Control)** 가 표준입니다. JFR은 JVM에 내장된 매우 가벼운(< 2% 오버헤드) 프로파일러로, 운영 환경에서 항상 켜놓는 사례가 늘고 있습니다.
JFR 녹화 시작
jcmd <pid> JFR.start name=profile duration=60s filename=profile.jfr
또는 JVM 시작 인자로
java -XX:StartFlightRecording=duration=60s,filename=profile.jfr -jar app.jar
JMC로 분석
jmc profile.jfr
JFR/JMC는 GC pause, allocation hot path, lock contention, IO bottleneck을 한 번에 보여줘 production-grade 성능 디버깅의 사실상 표준입니다.
Java 11부터 도입된 **Async Stack Traces**는 `CompletableFuture`/리액티브 체인의 비동기 콜백을 가상으로 이어 붙여 한 줄로 보여줍니다. Project Loom의 **Virtual Threads**(Java 21+)는 디버거에 새로운 도전을 던졌으며, IntelliJ 2025.1부터는 가상 스레드를 별도 패널로 시각화합니다.
14. 데이터베이스 디버깅 — EXPLAIN ANALYZE와 친구들
서비스 성능 버그의 절반은 데이터베이스에 있고, 그 절반은 잘못된 쿼리 플랜 때문입니다. PostgreSQL의 `EXPLAIN ANALYZE`는 실제 실행 통계를 함께 보여주는 가장 강력한 디버깅 명령입니다.
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT u.id, u.email, COUNT(o.id) AS orders
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at > NOW() - INTERVAL '30 days'
GROUP BY u.id, u.email
ORDER BY orders DESC
LIMIT 100;
출력에서 봐야 할 것은 **Seq Scan vs Index Scan**, **Rows planned vs actual**(차이가 크면 통계 부정확), **Buffers: shared hit / read**(IO 비용), **Sort method: external merge**(메모리 부족으로 디스크 정렬) 같은 신호입니다.
**pgBadger**(`pgbadger.darold.net`)는 PostgreSQL 슬로우 쿼리 로그를 분석해 HTML 리포트를 생성하며, **pg_stat_statements** extension은 누적 통계를 실시간으로 보여 줍니다.
MySQL은 `EXPLAIN FORMAT=JSON`과 8.0+의 `EXPLAIN ANALYZE`가 있고, **MySQL Performance Schema**가 누적 통계를 제공합니다.
ORM 사용자라면 ORM이 생성한 실제 SQL을 로깅하는 것이 첫 번째 디버깅 도구입니다. Hibernate의 `show_sql`, Prisma의 `log: ['query']`, Drizzle의 `logger: true`, ActiveRecord의 `ActiveRecord::Base.logger`가 그 진입점입니다.
15. 분산 시스템 디버깅 — OpenTelemetry, 구조화된 로그
마이크로서비스에서 버그는 한 프로세스 안에 갇히지 않습니다. 한 요청이 5~50개 서비스를 거치면서, 어디서 무엇이 실패했는지를 추적해야 하는데, 단일 노드 디버거로는 불가능합니다.
**OpenTelemetry**(`opentelemetry.io`)는 분산 추적·로그·메트릭의 사실상 표준 SDK입니다. 코드에 인스트루멘테이션을 한 번 심으면, 모든 호출이 trace ID를 공유한 채로 Jaeger, Tempo, Honeycomb, Datadog, New Relic 등으로 보내집니다.
const tracer = trace.getTracer('my-service')
async function handleRequest(req: Request) {
return tracer.startActiveSpan('handleRequest', async (span) => {
span.setAttribute('user_id', req.userId)
try {
const result = await processRequest(req)
span.setStatus({ code: 1 }) // OK
return result
} catch (err) {
span.recordException(err as Error)
span.setStatus({ code: 2, message: (err as Error).message }) // ERROR
throw err
} finally {
span.end()
}
})
}
**구조화 로그(Structured Logging)** 는 같은 trace ID를 키로 모든 서비스의 로그를 한 줄로 join할 수 있게 합니다. JSON 포맷(`pino`, `winston`, `zap`, `logrus`, `slog`)이 표준이고, Datadog Logs·Grafana Loki·Elastic Cloud가 백엔드입니다.
분산 디버깅의 가장 큰 함정은 **clock skew**입니다. 두 노드의 시계가 50ms 차이가 나면, 인과 관계가 뒤집힌 것처럼 보이는 트레이스가 나옵니다. 그래서 OpenTelemetry는 시계 대신 trace context(span ID 부모-자식 관계)를 사용합니다.
16. eBPF 디버깅 — bpftrace, BCC, Pixie
**eBPF**(`ebpf.io`)는 Linux 커널에 안전하게 코드를 주입할 수 있는 기술로, 디버깅·관측·보안의 새로운 표준이 되고 있습니다. eBPF 기반 디버깅 도구의 매력은 **운영 시스템에 코드 변경 없이 거의 zero overhead로 동작**한다는 점입니다.
**bpftrace**(`bpftrace.org`)는 awk-style 1-liner 언어로 eBPF 프로그램을 작성합니다.
모든 syscall::open 호출 추적
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
Node.js 프로세스가 어떤 파일을 여는지
bpftrace -e 'tracepoint:syscalls:sys_enter_openat /comm == "node"/ { printf("%s\n", str(args->filename)); }'
TCP 연결 latency 분포
bpftrace -e 'kprobe:tcp_v4_connect { @start[tid] = nsecs; } kretprobe:tcp_v4_connect /@start[tid]/ { @dur = hist(nsecs - @start[tid]); delete(@start[tid]); }'
**BCC**(`github.com/iovisor/bcc`)는 Python·C로 더 복잡한 eBPF 도구를 작성하는 프레임워크로, `opensnoop`, `execsnoop`, `tcpconnect` 같은 기성 도구가 함께 제공됩니다.
**Pixie**(`pixielabs.ai`, CNCF Sandbox)는 Kubernetes에서 eBPF로 자동 관측·디버깅을 제공합니다. agent 설치만으로 모든 HTTP·gRPC·MySQL·Postgres 트래픽이 자동 캡처되어, 코드 변경 없이 디버깅이 가능합니다.
**Coroot**(`coroot.com`)는 eBPF + OpenTelemetry를 결합해 모든 마이크로서비스 사이의 호출 관계와 latency를 자동으로 매핑합니다.
17. 메모리 버그 — Valgrind · ASan · MSan · TSan · UBSan
C/C++/Rust unsafe 코드의 영원한 적은 **메모리 안전성 버그**(use-after-free, double-free, buffer overflow, uninitialized read, data race)입니다. 2026년 표준은 컴파일 시간 Sanitizer + 런타임 Valgrind의 조합입니다.
**Valgrind Memcheck**(`valgrind.org`)는 30년 가까운 역사를 가진 동적 분석기로, 모든 메모리 접근을 가로채 검증합니다.
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./app
오버헤드는 10~50배로 느리지만, 어떤 코드도 변경 없이 동작한다는 압도적 장점이 있습니다.
**ASan(AddressSanitizer)** 는 컴파일 시간 옵션 `-fsanitize=address`로 빌드해, 런타임 오버헤드 ~2배로 use-after-free·buffer overflow를 잡습니다.
clang -fsanitize=address -g -O1 main.c -o app
./app
**MSan(MemorySanitizer)**: 초기화되지 않은 메모리 읽기를 탐지 (`-fsanitize=memory`).
**TSan(ThreadSanitizer)**: data race 탐지 (`-fsanitize=thread`). 멀티스레드 디버깅의 필수 도구.
**UBSan(UndefinedBehaviorSanitizer)**: 정수 오버플로우, NULL 역참조, 정렬되지 않은 포인터 등 (`-fsanitize=undefined`).
이 네 가지 sanitizer는 CI 파이프라인에서 fast test suite과 함께 항상 켜놓는 것이 2026년의 표준 관행입니다. Chromium·Firefox·LLVM 자체가 매일 ASan·TSan 빌드를 돌립니다.
18. 성능 프로파일러 — perf · Instruments · VTune · pprof
성능 버그도 디버깅의 일부입니다. 빠른 코드를 만드는 가장 빠른 길은 **느린 부분을 측정**하는 것입니다.
**Linux perf**(`perf.wiki.kernel.org`)는 커널이 내장한 프로파일러로, hardware PMC(Performance Monitoring Counter)에 접근해 CPU·캐시·branch 미스를 측정합니다.
함수별 CPU time 측정
perf record -g ./app
perf report
Flame graph로 시각화
perf record -F 99 -g ./app -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > out.svg
**Instruments**(macOS, Xcode 일부)는 Apple의 표준 프로파일러로, Time Profiler·Allocations·Leaks·Network·System Trace 같은 템플릿을 제공합니다.
**Intel VTune**(`intel.com/vtune`)은 Intel CPU에 특화된 프로파일러로, micro-architectural 단위(cache miss, branch misprediction, vectorization opportunity)까지 분석합니다.
**pprof**(`github.com/google/pprof`)는 Google이 만든 프로파일 포맷이자 분석 도구로, Go의 빌트인 `runtime/pprof`, Rust의 `pprof-rs`, C++의 gperftools가 모두 호환됩니다.
// 다른 터미널에서
// go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30
19. DAP — Debug Adapter Protocol
여러 디버거가 있는데 IDE 하나로 모두 다룰 수 있는 비결은 **DAP(Debug Adapter Protocol)** 입니다(`microsoft.github.io/debug-adapter-protocol/`). Microsoft가 2017년에 발표한 표준으로, JSON-RPC 기반으로 IDE와 디버거 백엔드를 분리합니다.
표준 메시지는 `initialize`, `launch`, `attach`, `setBreakpoints`, `stackTrace`, `scopes`, `variables`, `evaluate`, `continue`, `next`, `stepIn`, `stepOut`, `terminate` 등입니다.
DAP를 지원하는 클라이언트는 VS Code, Vim의 `vimspector`, Neovim의 `nvim-dap`, Emacs의 `dap-mode`, JetBrains IDE, Sublime Text의 `Debugger` 등 거의 모든 모던 에디터입니다. 백엔드는 `debugpy`(Python), `node` inspector, `delve`(Go), `lldb-vscode`(C/C++), `js-debug`(Node/Chrome) 등이 있습니다.
VS Code의 `launch.json`은 본질적으로 DAP `launch` 메시지의 인자입니다. 클라이언트(IDE)와 백엔드(언어별 디버거)가 표준 프로토콜로 분리되어 있다는 점이 DAP의 핵심 가치입니다.
20. AI 디버깅 — Claude Code, Cursor, Copilot Workspace
2025~2026년 가장 큰 변화는 **AI 코딩 에이전트의 디버깅 능력**입니다.
**Claude Code**(`claude.com/code`)는 터미널 기반 에이전트로, 스택 트레이스·로그·테스트 출력을 그대로 붙여 넣으면 가설을 세우고, 코드를 읽고, 수정 제안을 합니다. 2025년 Claude 4.7부터는 Chrome DevTools MCP를 통해 실제 브라우저 디버깅도 직접 수행합니다.
**Cursor**(`cursor.com`)는 VS Code fork로, 인라인 에러 메시지에 "Fix with AI" 버튼을 제공합니다. 2026년 Cursor 1.0에서는 디버거 세션 자체와 통합되어, 브레이크포인트에서 멈춘 상태의 변수 값을 LLM이 직접 inspect하면서 가설을 생성합니다.
**GitHub Copilot Workspace**(`githubnext.com/projects/copilot-workspace`)는 이슈 → 코드 변경 → PR 전체 흐름을 자동화하는 실험적 프로덕트로, 디버깅 시나리오에서 "이 이슈를 재현하는 테스트를 작성하고 고쳐줘"가 가능합니다.
AI 디버깅의 한계도 명확합니다. **재현 불가능한 버그**(타이밍, 환경 의존), **거대한 코드베이스 전체 컨텍스트**, **외부 시스템 통합**(데이터베이스, 외부 API)이 필요한 디버깅은 여전히 인간이 주도해야 합니다. AI는 가설 생성·코드 읽기·반복 작업 자동화에 강하지만, root cause 결정은 마지막까지 사람이 합니다.
21. Reverse 디버깅 — UDB, rr, Pernosco, TTD 비교
타임 트래블·역방향 디버깅 도구를 한눈에 비교합니다.
| 도구 | 회사 | 플랫폼 | 가격 | 강점 |
|---|---|---|---|---|
| rr | Mozilla (오픈소스) | Linux (Intel) | 무료 | Firefox·Chromium 표준 |
| Pernosco | Pernosco Inc. | Linux + 클라우드 | 트레이스당 과금 | 클라우드 omniscient |
| WinDbg TTD | Microsoft | Windows | 무료 | 커널 디버깅 |
| UDB | Undo.io | Linux | 엔터프라이즈 | C++·Java 전문, AAA 게임 |
**UDB**(`undo.io`)는 영국 케임브리지의 Undo가 만든 엔터프라이즈 reverse 디버거로, EA·Riot Games·Bloomberg가 사용합니다. rr보다 더 다양한 CPU·플랫폼 지원과 IDE 통합이 강점입니다.
역방향 디버깅이 가장 빛나는 시나리오는 다음과 같습니다.
1. **NULL 포인터 추적**: `watch *ptr` + `reverse-continue` → 누가 NULL을 넣었는가
2. **상태 손상**: 잘못된 값이 보일 때 거꾸로 가서 누가 set 했는가
3. **비결정적 deadlock**: 한 번 녹화하면 무한 반복 가능
4. **테스트 flakiness**: CI에서 가끔 실패하는 테스트의 재현
22. Print 디버깅을 잘 하는 법
가장 오래되고, 가장 자주 쓰이고, 가장 과소평가된 디버깅 도구는 여전히 `print`입니다. `printf`, `console.log`, `eprintln!`, `dbg!`, `fmt.Println`. 이를 잘 쓰는 법:
1. **컨텍스트를 다 적어라**: 변수 이름, 값, 함수명, 라인 번호를 같이 출력. Rust `dbg!`, Go `log.Printf("[%s] x=%v", funcName, x)`가 모범.
2. **JSON 한 줄(JSON Lines)**: 사람이 읽고 grep·jq로 다시 분석 가능. `console.log(JSON.stringify({ event: 'request', userId, latency }))`.
3. **로그 레벨 사용**: trace/debug/info/warn/error. 운영에서 켜고 끌 수 있게.
4. **상관 ID(Correlation ID)**: 한 요청을 따라가는 모든 로그가 같은 ID를 공유.
5. **`console.dir` vs `console.log`**: 객체를 깊이 출력하려면 `console.dir(obj, { depth: null })`.
6. **Rust `tracing` crate**: 매크로로 span·event를 만들고, OpenTelemetry로 자동 export.
print 디버깅의 함정은 **로그 폭주**입니다. 잘못 켜놓고 잊으면 운영 환경에서 디스크가 차고, 로그 비용이 EC2 비용보다 커집니다. 환경 변수로 레벨을 제어하고, 한 줄 로그도 cardinality(다양한 값) 폭증을 경계해야 합니다.
23. 한국 사례 — 쿠팡 · 네이버 · 카카오
**쿠팡의 SRE 사후 분석(Post-mortem) 문화**는 한국 IT 업계에서 잘 알려져 있습니다. 2021년 AWS Tokyo 리전 장애로 발생한 부분 서비스 중단 이후, 쿠팡은 모든 P0/P1 인시던트에 대해 영문 post-mortem을 작성하고 사내에 공개하는 정책을 정착시켰습니다. 디버깅 측면에서 강조되는 것은 **타임라인 정확성**과 **5 Whys 기반 root cause 분석**입니다.
**네이버 D2 / SRE 블로그**(`d2.naver.com`, `tech-blog.naver.com`)는 자바·JVM·MySQL 디버깅 사례를 다년간 공개해 왔습니다. 특히 2024년 공개된 "Pinpoint 분산 트레이서로 마이크로서비스 장애 추적하기" 시리즈는 네이버 자체 트레이서 Pinpoint(`github.com/pinpoint-apm/pinpoint`, 오픈소스)의 실전 사용법을 다룹니다.
**카카오 카오톡 2022년 10월 화재 사태**는 한국 IT 역사상 가장 큰 디버깅·사후 분석 사례로 남았습니다. 데이터센터 한 곳의 화재로 카오톡·다음·카카오T 같은 서비스가 5~127시간 중단되었고, 이후 카카오는 multi-DC 액티브-액티브 아키텍처, 장애 대응 매뉴얼 전면 개정, 분산 트레이싱 의무화 같은 변화를 가져왔습니다. 그 과정과 디버깅 교훈은 카카오 테크 블로그(`tech.kakao.com`)에 다년간 시리즈로 공개되었습니다.
세 회사의 공통점은 **사후 분석을 자산으로 만드는 문화**입니다. 디버깅은 개인 기술이 아니라 조직의 학습 사이클로 작동해야 한다는 것이 한국 빅테크의 공통된 메시지입니다.
24. 일본 사례 — Cookpad · Mercari · Hatena · HSAS
**Cookpad Engineering Blog**(`techlife.cookpad.com`)는 Ruby·Rails 디버깅 사례를 일본에서 가장 많이 공개합니다. 특히 2024년 시리즈 "rbtrace로 운영 Ruby 프로세스 디버깅"은 Ruby의 `rbtrace`(`github.com/tmm1/rbtrace`)와 `rbspy`(`github.com/rbspy/rbspy`)를 활용한 무중단 진단을 다룹니다.
**Mercari Engineering Blog**(`engineering.mercari.com/en/blog/`)는 Go·Kubernetes 디버깅 사례가 풍부합니다. 2025년 "Debugging Goroutine Leaks at Mercari Scale" 포스트는 `pprof` + `goleak`를 결합한 자동 leak 감지를 다룹니다.
**Hatena Engineer Seminar**는 일본 엔지니어링 컨퍼런스 중 가장 오래된 시리즈로, Hatena의 Perl·Go·TypeScript 디버깅 노하우가 매년 공개됩니다.
**HSAS(High School / University Security Audit Society)** 와 일본 보안 커뮤니티는 GDB·radare2·Ghidra 같은 도구를 사용한 리버스 엔지니어링·exploit 디버깅 영역에서 매우 활발합니다. SECCON·DEF CON CTF 일본팀들이 사용하는 디버깅 워크플로는 시스템·바이너리 디버깅의 첨단을 보여 줍니다.
25. 참고 / References
- *Why Programs Fail* (Zeller, 2009/2023 2판) — 가설 기반 디버깅의 정전
- GDB documentation — `https://sourceware.org/gdb/current/onlinedocs/gdb/`
- GDB Python API — `https://sourceware.org/gdb/current/onlinedocs/gdb/Python.html`
- gdb-dashboard — `https://github.com/cyrus-and/gdb-dashboard`
- LLDB documentation — `https://lldb.llvm.org/`
- LLDB Python reference — `https://lldb.llvm.org/python_reference/index.html`
- rr project — `https://rr-project.org/`
- Pernosco — `https://pernos.co/`
- WinDbg TTD — `https://learn.microsoft.com/windows-hardware/drivers/debugger/time-travel-debugging-overview`
- Chrome DevTools — `https://developer.chrome.com/docs/devtools/`
- Chrome DevTools MCP — `https://github.com/ChromeDevTools/chrome-devtools-mcp`
- Firefox DevTools — `https://firefox-source-docs.mozilla.org/devtools-user/`
- Safari Web Inspector — `https://webkit.org/web-inspector/`
- Python pdb — `https://docs.python.org/3/library/pdb.html`
- pdbpp — `https://github.com/pdbpp/pdbpp`
- ipdb — `https://github.com/gotcha/ipdb`
- pudb — `https://github.com/inducer/pudb`
- debugpy — `https://github.com/microsoft/debugpy`
- Bun docs — `https://bun.sh/docs/runtime/debugger`
- Deno docs — `https://docs.deno.com/runtime/manual/basics/debugging_your_code`
- Node.js inspector — `https://nodejs.org/api/debugger.html`
- Delve — `https://github.com/go-delve/delve`
- tokio-console — `https://github.com/tokio-rs/console`
- cargo-flamegraph — `https://github.com/flamegraph-rs/flamegraph`
- pprof-rs — `https://github.com/tikv/pprof-rs`
- JDK Mission Control — `https://www.oracle.com/java/technologies/jdk-mission-control.html`
- Java Flight Recorder — `https://docs.oracle.com/en/java/javase/21/jfapi/`
- OpenTelemetry — `https://opentelemetry.io/`
- bpftrace — `https://bpftrace.org/`
- BCC — `https://github.com/iovisor/bcc`
- Pixie — `https://github.com/pixie-io/pixie`
- Coroot — `https://coroot.com/`
- Valgrind — `https://valgrind.org/`
- AddressSanitizer — `https://github.com/google/sanitizers/wiki/AddressSanitizer`
- ThreadSanitizer — `https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual`
- Linux perf — `https://perf.wiki.kernel.org/`
- pprof — `https://github.com/google/pprof`
- Debug Adapter Protocol — `https://microsoft.github.io/debug-adapter-protocol/`
- Pinpoint APM — `https://github.com/pinpoint-apm/pinpoint`
- Cookpad Techlife — `https://techlife.cookpad.com/`
- Mercari Engineering — `https://engineering.mercari.com/en/blog/`
- 카카오 테크 블로그 — `https://tech.kakao.com/`
현재 단락 (1/347)
소프트웨어 엔지니어가 평생 가장 많이 하는 일은 코드를 작성하는 것이 아니라 **버그를 디버깅(Debugging)** 하는 것입니다. 다양한 통계가 그 비중을 25%에서 50% 사...