[Python] `pdb`와 데코레이터 활용 가이드: 특정 함수만 효율적으로 디버깅하기
데코레이터와 pdb
는 함께 사용하면 디버깅 작업을 크게 효율화할 수 있습니다. 데코레이터를 통해 특정 함수에만 자동으로 pdb
를 적용할 수 있어, 코드 수정 없이 필요한 함수에 디버깅 모드를 설정할 수 있습니다.
이번 가이드에서는 pdb
와 데코레이터를 함께 활용해 특정 함수에만 자동으로 디버거를 활성화하는 방법과, 실전에서 사용할 수 있는 예제를 소개합니다.
1. pdb
를 자동으로 활성화하는 디버깅 데코레이터
아래 데코레이터는 디버깅 모드가 필요한 함수에만 자동으로 pdb
를 적용하는 방법을 보여줍니다. 이 데코레이터는 함수가 호출될 때마다 자동으로 중단점을 설정하여 pdb
디버깅 모드로 진입하도록 합니다.
import pdb
from functools import wraps
def debug_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[DEBUG] Entering function '{func.__name__}' with args={args}, kwargs={kwargs}")
pdb.set_trace() # 디버깅 모드 활성화
result = func(*args, **kwargs)
print(f"[DEBUG] Exiting function '{func.__name__}' with result={result}")
return result
return wrapper
@debug_decorator
def calculate_total(price, tax):
total = price + (price * tax)
return total
# 실제 실행
calculate_total(100, 0.1)
설명: @debug_decorator
데코레이터는 calculate_total
함수가 호출될 때마다 pdb
를 활성화하여, 함수 내에서 변수를 확인하고 코드 흐름을 추적할 수 있도록 합니다. 함수 입출력 상태를 자동으로 기록하므로, 함수의 시작과 종료 시점에서 변수 상태를 추적하기에 유용합니다.
2. 특정 조건에서만 pdb
디버깅 활성화하기
때로는 특정 조건을 충족할 때만 디버거가 활성화되도록 설정하고 싶을 때가 있습니다. 예를 들어, 특정 인자를 받을 때만 디버깅 모드를 활성화하도록 데코레이터에 조건을 추가할 수 있습니다.
def conditional_debug_decorator(condition):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if condition(*args, **kwargs):
print(f"[CONDITIONAL DEBUG] '{func.__name__}' activated for debugging.")
pdb.set_trace() # 조건을 만족할 때만 디버거 활성화
return func(*args, **kwargs)
return wrapper
return decorator
# 특정 조건 함수: tax가 0.1일 때만 디버깅 활성화
def debug_condition(price, tax):
return tax == 0.1
@conditional_debug_decorator(debug_condition)
def calculate_discounted_total(price, tax):
total = price + (price * tax)
return total
# 실제 실행
calculate_discounted_total(100, 0.1) # 조건에 맞아 디버깅 활성화
calculate_discounted_total(100, 0.2) # 디버깅 비활성화
설명: conditional_debug_decorator
는 인자가 특정 조건을 만족할 때만 pdb
를 활성화합니다. 이렇게 조건부 디버깅을 설정하면 특정 상황에서만 함수 내부를 집중적으로 추적할 수 있습니다. 위 코드에서는 tax
가 0.1
일 때만 디버깅 모드로 진입하도록 설정했습니다.
3. 오류 발생 시 자동 디버깅 모드 활성화 데코레이터
코드가 오류를 발생시킬 때 자동으로 pdb
디버깅 모드로 진입하게 하는 데코레이터입니다. 이 방법은 예기치 않은 예외가 발생할 경우 문제가 발생한 즉시 디버깅을 시작할 수 있어 매우 유용합니다.
def error_debug_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"[ERROR DEBUG] Exception in '{func.__name__}': {e}")
pdb.set_trace() # 예외 발생 시 디버거 활성화
raise e # 예외를 다시 발생시켜 호출자에게 전달
return wrapper
@error_debug_decorator
def risky_division(a, b):
return a / b
# 실제 실행
risky_division(10, 2) # 정상 실행
risky_division(10, 0) # ZeroDivisionError 발생 시 디버깅 모드 진입
설명: @error_debug_decorator
는 함수 실행 중 예외가 발생했을 때 자동으로 pdb
를 활성화해, 오류 발생 시점에서 변수 상태와 코드 흐름을 즉시 확인할 수 있습니다. 이 데코레이터는 예외 디버깅에 매우 유용합니다.
4. 함수 호출 흐름을 한눈에 살펴보는 pdb
와 로깅 데코레이터 결합
다양한 함수가 서로 호출될 때 함수 호출 순서를 확인하는 것은 매우 중요합니다. 아래는 각 함수 호출을 기록하고, 필요할 때 pdb
로 중단해 호출 흐름과 변수 상태를 한꺼번에 추적하는 데코레이터입니다.
def trace_and_debug_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[TRACE] Entering '{func.__name__}' with args={args}, kwargs={kwargs}")
pdb.set_trace() # 함수 호출 시 디버깅 활성화
result = func(*args, **kwargs)
print(f"[TRACE] Exiting '{func.__name__}' with result={result}")
return result
return wrapper
@trace_and_debug_decorator
def add(a, b):
return a + b
@trace_and_debug_decorator
def multiply(x, y):
return x * y
@trace_and_debug_decorator
def main():
result1 = add(3, 5)
result2 = multiply(result1, 10)
return result2
# 실제 실행
main()
설명: trace_and_debug_decorator
는 함수가 호출될 때마다 pdb
를 활성화하여 각 함수의 입출력을 기록하고, 함수 호출 흐름과 변수를 추적할 수 있도록 합니다. 이 방법은 서로 호출하는 여러 함수에서 코드 흐름을 추적할 때 특히 유용합니다.
결론: pdb
와 데코레이터로 디버깅을 자동화하고 효율화하기
이제 pdb
와 데코레이터를 결합해 필요한 함수에만 자동으로 디버거를 적용하거나, 조건부로 디버깅을 활성화하는 방법을 알게 되었습니다. 이 방식은 코드 수정 없이 필요한 함수에만 pdb
를 적용할 수 있어, 더욱 유연하고 효율적인 디버깅을 가능하게 합니다.
이제 print
대신 pdb
와 데코레이터를 통해 디버깅을 체계화하고 자동화해보세요!