pdb 기초: 파이썬 내장 디버거 시작하기

표준 라이브러리 pdb로 코드를 한 줄씩 따라가며 변수를 들여다보는 법. set_trace와 breakpoint, n·s·c·l·p 같은 핵심 명령어, 호출 스택 이동까지 정리합니다.

· 6 min read · PALDYN Team

지난 글에서 테스트가 어떻게 발견되고 실행되는지까지 살펴보며 테스트 여정을 마무리했다. 그런데 테스트가 빨갛게 실패하거나, 운영 중인 코드가 예상과 다르게 동작할 때 우리는 결국 같은 질문 앞에 선다. “지금 이 변수에 도대체 무슨 값이 들어 있는가?” print를 여기저기 흩뿌려 답을 찾을 수도 있지만, 파이썬에는 실행을 멈춰 세우고 그 자리에서 코드를 한 줄씩 걸어 다니며 모든 변수를 들여다볼 수 있는 도구가 기본으로 들어 있다. 바로 표준 라이브러리 디버거 pdb다.

실행을 멈춰 세우는 한 줄

pdb를 쓰는 가장 간단한 방법은 멈추고 싶은 지점에 breakpoint()를 적는 것이다. 파이썬 3.7부터 들어온 내장 함수로, 그 줄에 도달하는 순간 인터프리터가 멈추고 (Pdb) 프롬프트가 뜬다. 이제부터는 우리가 키를 쥔다.

def calculate_total(items):
    total = 0
    for item in items:
        breakpoint()        # 여기서 실행이 멈춘다
        total += item.price
    return total

이 줄에 도달하면 터미널에 (Pdb) 가 나타나고, 프로그램은 그 자리에 얼어붙는다. 우리가 명령을 입력하기 전까지는 단 한 줄도 더 나아가지 않는다. 예전 코드에서는 import pdb; pdb.set_trace()라는 두 줄을 썼는데, 이제는 breakpoint() 한 줄이면 충분하다.

핵심 명령어 몇 개면 충분하다

프롬프트가 뜨면 막막할 수 있지만, 실무에서 90%는 대여섯 개의 명령어로 해결된다. 한 글자짜리 명령이라 손에 금방 익는다.

pdb 핵심 명령어

가장 자주 쓰는 건 n(next)과 s(step)다. n은 다음 줄로 넘어가되 함수 호출은 통째로 실행하고, s는 함수 호출이 있으면 그 안으로 따라 들어간다. “이 함수가 의심스럽다” 싶으면 s로 들어가고, “이 함수는 믿으니 결과만 보자” 싶으면 n으로 건너뛴다.

(Pdb) p item.price      # 변수 값을 출력
1500
(Pdb) l                 # 현재 위치 주변 소스 보기
(Pdb) n                 # 다음 줄로
(Pdb) c                 # 다음 중단점까지 쭉 실행

p expr는 식의 값을 출력하고, pp expr는 딕셔너리나 리스트를 보기 좋게 들여쓰기해 출력한다. l(list)은 지금 멈춘 위치 주변의 소스 코드를 보여 줘서 “내가 어디에 있더라”를 잡아 준다. 다 봤으면 c(continue)로 다음 중단점까지 한 번에 달리거나, q(quit)로 디버거를 빠져나온다.

한 줄씩 걸으며 상태를 관찰한다

디버깅의 본질은 결국 단순하다. 멈춰 세우고, 값을 확인하고, 한 줄 나아가고, 다시 값을 확인한다. 이 리듬을 반복하다 보면 “내 머릿속 모델”과 “코드의 실제 동작”이 갈라지는 정확한 지점이 드러난다. 버그란 대개 그 갈라지는 한 줄에 숨어 있다.

디버깅 세션의 흐름

(Pdb) 프롬프트에서는 변수 출력뿐 아니라 임의의 파이썬 식을 평가할 수도 있다. len(items)를 입력해 길이를 보거나, [x.price for x in items]로 전체를 펼쳐 볼 수도 있다. 디버거 안은 그 시점의 살아 있는 파이썬 셸인 셈이다.

(Pdb) [x.price for x in items]
[1500, 2300, 800]
(Pdb) w
  /app/order.py(12)calculate_total()
-> total += item.price
(Pdb) u

w(where)는 지금 어떤 호출 경로를 거쳐 여기까지 왔는지 호출 스택을 보여 준다. u(up)와 d(down)으로 스택을 오르내리면, 이 함수를 호출한 바깥쪽 함수의 변수까지 들여다볼 수 있다. “이 인자가 어디서 잘못 넘어왔지?”를 추적할 때 결정적인 단서가 된다.

명령행에서 바로 띄우기

코드를 건드리지 않고 디버거로 들어가는 방법도 있다. python -m pdb script.py로 실행하면 프로그램 첫 줄부터 디버거가 붙는다. 예외가 나서 죽는 스크립트라면, 이 방식이 특히 유용하다. 죽기 직전의 상태를 그대로 붙잡을 수 있기 때문이다.

python -m pdb app.py
# 또는 예외가 난 직후 사후 분석(post-mortem)
python -m pdb -c continue app.py

pdb는 화려하지 않지만 어디에나 있다. 추가 설치도, 의존성도 필요 없이 파이썬이 깔린 곳이라면 어디서든 똑같이 작동한다. print로 한참 헤매던 문제가 breakpoint() 한 줄과 n, p 몇 번으로 풀리는 경험을 한 번 하고 나면, 디버거는 더 이상 거창한 도구가 아니라 손에 익은 일상 도구가 된다.


지난 글: 테스트 디스커버리: 도구가 테스트를 찾는 규칙

다음 글: breakpoint(): 디버거를 부르는 표준 한 줄


읽어주셔서 감사합니다. 😊