KR_Debugging - somaz94/python-study GitHub Wiki
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋๋ฒ๊น ๋ฐฉ๋ฒ์ด๋ค.
def calculate_total(items):
total = 0
for item in items:
print(f"ํ์ฌ ์์ดํ
: {item}") # ๋๋ฒ๊น
์ฉ ์ถ๋ ฅ
total += item
print(f"ํ์ฌ ํฉ๊ณ: {total}") # ๋๋ฒ๊น
์ฉ ์ถ๋ ฅ
return total
# ์คํ
items = [1, 2, 3, 4, 5]
result = calculate_total(items)
print(f"์ต์ข
๊ฒฐ๊ณผ: {result}")
โ ํน์ง:
- ๊ฐ๋จํ ๊ตฌํ
- ์ฆ๊ฐ์ ์ธ ํ์ธ
- ์ฝ๋ ํ๋ฆ ์ถ์
โ ๊ณ ๊ธ print ๋๋ฒ๊น ๊ธฐ๋ฒ:
- ๊ตฌ๋ถ์ ์ฌ์ฉ:
print("===DEBUG===", var)
- ์กฐ๊ฑด๋ถ ๋๋ฒ๊น
:
if debug_mode: print(var)
- f-string ํํ์:
print(f"{var=}")
(Python 3.8+) - ์ปจํ
์คํธ ์ถ๊ฐ:
print(f"[ํจ์: {func_name}] ๊ฐ: {value}")
Python ๋ด์ฅ ๋๋ฒ๊ฑฐ์ธ pdb๋ฅผ ํ์ฉํ ๋ํํ ๋๋ฒ๊น ๋ฐฉ๋ฒ์ด๋ค.
import pdb
def complex_function(data):
result = []
for item in data:
# ๋ธ๋ ์ดํฌํฌ์ธํธ ์ค์
pdb.set_trace() # Python 3.7 ์ดํ
# breakpoint() # Python 3.7 ์ด์
processed = item * 2
result.append(processed)
return result
# pdb ๋ช
๋ น์ด
"""
n(next): ๋ค์ ์ค ์คํ
s(step): ํจ์ ๋ด๋ถ๋ก ๋ค์ด๊ฐ๊ธฐ
c(continue): ๋ค์ ๋ธ๋ ์ดํฌํฌ์ธํธ๊น์ง ์คํ
p variable: ๋ณ์ ์ถ๋ ฅ
l(list): ํ์ฌ ์์น ์ฃผ๋ณ ์ฝ๋ ์ถ๋ ฅ
w(where): ์ฝ์คํ ์ถ๋ ฅ
q(quit): ๋๋ฒ๊ฑฐ ์ข
๋ฃ
"""
โ ํน์ง:
- ๋ํํ ๋๋ฒ๊น
- ์ํ ๊ฒ์ฌ
- ๋จ๊ณ๋ณ ์คํ
โ ๊ณ ๊ธ pdb ๊ธฐ๋ฅ:
- ์กฐ๊ฑด๋ถ ๋ธ๋ ์ดํฌํฌ์ธํธ:
pdb.set_trace() if condition else None
- ํฌ์คํธ๋ชจํ
๋๋ฒ๊น
:
python -m pdb your_script.py
- ๋ช ๋ น์ด ๋ฐ๋ณต: Enter ํค๋ก ๋ง์ง๋ง ๋ช ๋ น์ด ๋ฐ๋ณต
- ํํ์ ํ๊ฐ:
!expression
์ผ๋ก ํํ์ ์ง์ ์คํ - ์ฝ๋ ์คํ:
!
์ ๋์ฌ๋ฅผ ์ฌ์ฉํ์ฌ Python ๋ช ๋ น ์คํ
ํตํฉ ๊ฐ๋ฐ ํ๊ฒฝ(IDE)์์ ์ ๊ณตํ๋ ๋๋ฒ๊น ๋๊ตฌ๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def get_info(self):
# IDE์์ ๋ธ๋ ์ดํฌํฌ์ธํธ ์ค์
info = f"์ด๋ฆ: {self.name}, ๋์ด: {self.age}"
processed_info = self.process_info(info)
return processed_info
def process_info(self, info):
# ์ฌ๊ธฐ์๋ ๋ธ๋ ์ดํฌํฌ์ธํธ ์ค์ ๊ฐ๋ฅ
return info.upper()
โ ํน์ง:
- ์๊ฐ์ ๋๋ฒ๊น
- ๋ณ์ ๊ฐ์
- ์ฝ์คํ ํ์ธ
โ ์ฃผ์ IDE ๋๋ฒ๊น ๊ธฐ๋ฅ:
- VS Code: ์กฐ๊ฑด๋ถ ๋ธ๋ ์ดํฌํฌ์ธํธ, ๋ณ์ ๊ฐ์, ํธ์ถ ์คํ ๋ณด๊ธฐ
- PyCharm: ์๊ฒฉ ๋๋ฒ๊น , ๋ค์ค ์ค๋ ๋ ๋๋ฒ๊น , ํํ์ ํ๊ฐ
- Jupyter Notebook:
%debug
๋งค์ง ๋ช ๋ น์ด๋ก ์์ธ ๋ฐ์ ์ง์ ๋๋ฒ๊น - Spyder: ๋ณ์ ํ์๊ธฐ, ์ง์ ์คํ ์ฝ์
๋ก๊น ๋ชจ๋์ ์ฌ์ฉํ์ฌ ์ฒด๊ณ์ ์ผ๋ก ๋๋ฒ๊น ์ ๋ณด๋ฅผ ๊ธฐ๋กํ๋ ๋ฐฉ๋ฒ์ด๋ค.
import logging
# ๋ก๊น
์ค์
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='debug.log'
)
logger = logging.getLogger(__name__)
def process_data(data):
logger.debug(f"๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์: {data}")
try:
result = data * 2
logger.info(f"๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋ฃ: {result}")
return result
except Exception as e:
logger.error(f"์๋ฌ ๋ฐ์: {str(e)}")
raise
โ ํน์ง:
- ์๊ตฌ์ ์ธ ๊ธฐ๋ก
- ๋ ๋ฒจ๋ณ ๋ก๊น
- ํ์ผ ์ถ๋ ฅ ์ง์
โ ๊ณ ๊ธ ๋ก๊น ๊ธฐ๋ฒ:
- ๋ก๊ฑฐ ๊ณ์ธต ๊ตฌ์กฐ ํ์ฉ
- ์ฌ๋ฌ ํธ๋ค๋ฌ ์ฌ์ฉ (ํ์ผ, ์ฝ์, ์ด๋ฉ์ผ)
- ๋ก๊ทธ ํฌ๋งท ์ปค์คํฐ๋ง์ด์ง
- ๋ก๊ทธ ํ์ (rotation) ์ค์
- ์ปจํ ์คํธ ์ ๋ณด ํฌํจ
# ๊ณ ๊ธ ๋ก๊น
์ค์ ์์
import logging
from logging.handlers import RotatingFileHandler
import traceback
# ๋ก๊ฑฐ ์์ฑ
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# ํ์ผ ํธ๋ค๋ฌ (ํฌ๊ธฐ ๊ธฐ๋ฐ ๋กํ
์ด์
)
file_handler = RotatingFileHandler('app.log', maxBytes=1024*1024, backupCount=5)
file_handler.setLevel(logging.INFO)
# ์ฝ์ ํธ๋ค๋ฌ
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
# ํฌ๋งท ์ง์
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# ํธ๋ค๋ฌ ์ถ๊ฐ
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# ๋ก๊น
์ฌ์ฉ ์
def complex_operation(x, y):
logger.debug(f"complex_operation ์์: x={x}, y={y}")
try:
result = x / y
logger.info(f"๊ณ์ฐ ๊ฒฐ๊ณผ: {result}")
return result
except Exception as e:
logger.error(f"์๋ฌ ๋ฐ์: {str(e)}\n{traceback.format_exc()}")
raise
๋ค์ํ ๋๋ฒ๊น ๋๊ตฌ๋ฅผ ํ์ฉํ์ฌ ํจ์จ์ ์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด๋ค.
# Python Debugger (ipdb)
"""
pip install ipdb
import ipdb; ipdb.set_trace()
"""
# pudb (์๊ฐ์ ๋๋ฒ๊ฑฐ)
"""
pip install pudb
import pudb; pudb.set_trace()
"""
# ๋ณ์ ๊ฒ์ฌ
from pprint import pprint
def inspect_object(obj):
print("\n== ๊ฐ์ฒด ์ ๋ณด ==")
pprint(vars(obj))
print("\n== ํ์
์ ๋ณด ==")
pprint(dir(obj))
โ ํน์ง:
- ๋ค์ํ ๋๊ตฌ ์ง์
- ์์ธํ ๊ฐ์ฒด ๊ฒ์ฌ
- ์๊ฐํ ๊ธฐ๋ฅ
โ ์ถ๊ฐ ๋๋ฒ๊น ๋๊ตฌ:
-
icecream
:from icecream import ic; ic(variable)
ํํ๋ก ๊ฐํธํ ๋๋ฒ๊น ์ถ๋ ฅ -
pytest
: ํ ์คํธ ๊ธฐ๋ฐ ๋๋ฒ๊น ์ ์ํ ํ๋ ์์ํฌ -
trace
: ์ฝ๋ ์คํ ์ถ์ ๋ฐ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์ -
memory-profiler
: ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋ถ์ -
line_profiler
: ๋ผ์ธ๋ณ ์คํ ์๊ฐ ๋ถ์
์์ธ ์ฒ๋ฆฌ๋ฅผ ํ์ฉํ ํจ๊ณผ์ ์ธ ๋๋ฒ๊น ์ ๋ต์ด๋ค.
def robust_function(data):
try:
result = process_data(data)
return result
except ValueError as e:
print(f"๊ฐ ์ค๋ฅ: {str(e)}")
# ๋๋ฒ๊น
์ ์ํ ์ถ๊ฐ ์ ๋ณด
print(f"๋ฌธ์ ๋ฐ์ดํฐ: {data}")
return None
except TypeError as e:
print(f"ํ์
์ค๋ฅ: {str(e)}")
# ํ์
์ ๋ณด ์ถ๋ ฅ
print(f"๋ฐ์ดํฐ ํ์
: {type(data)}")
return None
except Exception as e:
# ์์์น ๋ชปํ ์์ธ์ ์ ์ฒด ์คํ ํธ๋ ์ด์ค ์ถ๋ ฅ
import traceback
print(f"์์์น ๋ชปํ ์ค๋ฅ: {str(e)}")
traceback.print_exc()
return None
โ ํจ๊ณผ์ ์ธ ์์ธ ์ฒ๋ฆฌ ๋๋ฒ๊น ์ ๋ต:
- ๊ตฌ์ฒด์ ์ธ ์์ธ ํฌ์ฐฉ
- ์ปจํ ์คํธ ์ ๋ณด ๋ก๊น
- ๋ณต๊ตฌ ๋ฉ์ปค๋์ฆ ๊ตฌํ
- ์คํ ํธ๋ ์ด์ค ๋ถ์
- ์ฌ์ฉ์ ์ ์ ์์ธ ํ์ฉ
# ์ฌ์ฉ์ ์ ์ ์์ธ ์์
class DataValidationError(Exception):
def __init__(self, message, invalid_fields=None):
super().__init__(message)
self.invalid_fields = invalid_fields or []
self.timestamp = datetime.now()
def __str__(self):
return f"{super().__str__()} (์๋ชป๋ ํ๋: {self.invalid_fields}, ์๊ฐ: {self.timestamp})"
# ์ฌ์ฉ ์์
def validate_user_data(user_data):
invalid_fields = []
if not isinstance(user_data.get('age'), int):
invalid_fields.append('age')
if not isinstance(user_data.get('name'), str):
invalid_fields.append('name')
if invalid_fields:
raise DataValidationError("์ฌ์ฉ์ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ ์คํจ", invalid_fields)
return True
์ฝ๋์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ฐพ๊ณ ์ต์ ํํ๊ธฐ ์ํ ๋๋ฒ๊น ๊ธฐ๋ฒ์ด๋ค.
# ์๊ฐ ์ธก์
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} ์คํ ์๊ฐ: {end_time - start_time:.6f}์ด")
return result
return wrapper
@measure_time
def slow_function(size):
result = []
for i in range(size):
result.append(i ** 2)
return result
# ํ๋กํ์ผ๋ง
import cProfile
def profile_function(func, *args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
profiler.print_stats(sort='cumtime')
return result
# ์ฌ์ฉ ์์
profile_function(slow_function, 10000)
โ ์ฑ๋ฅ ๋๋ฒ๊น ๋๊ตฌ:
-
cProfile
: ํจ์ ํธ์ถ ํต๊ณ -
timeit
: ์ฝ๋ ๋ธ๋ก ์คํ ์๊ฐ ์ธก์ -
line_profiler
: ๋ผ์ธ๋ณ ์คํ ์๊ฐ ๋ถ์ -
memory_profiler
: ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋ถ์ -
objgraph
: ๊ฐ์ฒด ์ฐธ์กฐ ์๊ฐํ
# ๊ณ ๊ธ ์๊ฐ ์ธก์ ์ปจํ
์คํธ ๋งค๋์
import time
from contextlib import contextmanager
@contextmanager
def timer(name="์์
"):
start_time = time.time()
yield
elapsed = time.time() - start_time
print(f"{name} ์๋ฃ: {elapsed:.6f}์ด")
# ์ฌ์ฉ ์์
with timer("๋ฐ์ดํฐ ์ฒ๋ฆฌ"):
result = process_large_dataset()
๋ฉํฐ์ค๋ ๋ฉ, ๋ฉํฐํ๋ก์ธ์ฑ, ๋น๋๊ธฐ ์ฝ๋์ ๋๋ฒ๊น ๊ธฐ๋ฒ์ด๋ค.
import threading
import logging
# ์ค๋ ๋ ๋๋ฒ๊น
์ ์ํ ๋ก๊น
์ค์
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(threadName)s - %(message)s'
)
def thread_function(name, data):
logging.debug(f"์ค๋ ๋ {name} ์์")
try:
# ์์
์ํ
result = process_data(data)
logging.debug(f"์ค๋ ๋ {name} ๊ฒฐ๊ณผ: {result}")
except Exception as e:
logging.error(f"์ค๋ ๋ {name} ์ค๋ฅ: {str(e)}")
finally:
logging.debug(f"์ค๋ ๋ {name} ์ข
๋ฃ")
# ๋น๋๊ธฐ ์ฝ๋ ๋๋ฒ๊น
import asyncio
async def async_function(name, data):
print(f"๋น๋๊ธฐ ํจ์ {name} ์์")
try:
# ๋น๋๊ธฐ ์์
์๋ฎฌ๋ ์ด์
await asyncio.sleep(1)
result = data * 2
print(f"๋น๋๊ธฐ ํจ์ {name} ๊ฒฐ๊ณผ: {result}")
return result
except Exception as e:
print(f"๋น๋๊ธฐ ํจ์ {name} ์ค๋ฅ: {str(e)}")
raise
โ ๋ณ๋ ฌ ์ฝ๋ ๋๋ฒ๊น ์ ๋ต:
- ์ค๋ ๋๋ณ ๋ก๊น ๊ตฌ๋ถ
- ๊ฒฝ์ ์กฐ๊ฑด ์๋ณ
- ๋ฐ๋๋ฝ ๊ฐ์ง
- ๊ณต์ ์ํ ๋ชจ๋ํฐ๋ง
- ์ค๋ ๋ ๋คํ ๋ถ์
โ ๋น๋๊ธฐ ์ฝ๋ ๋๋ฒ๊น ์ ๋ต:
- ์ด๋ฒคํธ ๋ฃจํ ๋ชจ๋ํฐ๋ง
- ์ฝ๋ฃจํด ์ถ์
- ๋น๋๊ธฐ ์ปจํ ์คํธ ๋ก๊น
- ํ์คํฌ ์ํ ๊ฒ์ฌ
- ์์ธ ์ ํ ์ถ์
์ค์ ์ด์ ํ๊ฒฝ์์์ ๋ฌธ์ ๋ฅผ ๋๋ฒ๊น ํ๋ ๊ธฐ๋ฒ์ด๋ค.
# ๋ก๊น
๊ธฐ๋ฐ ์๊ฒฉ ๋๋ฒ๊น
import logging
import json
import socket
class ContextFilter(logging.Filter):
def __init__(self):
self.hostname = socket.gethostname()
def filter(self, record):
record.hostname = self.hostname
return True
# ์๊ฒฉ ๋ก๊น
์ค์
def setup_remote_logging():
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# ์ปจํ
์คํธ ํํฐ ์ถ๊ฐ
context_filter = ContextFilter()
logger.addFilter(context_filter)
# JSON ํฌ๋งทํฐ
class JsonFormatter(logging.Formatter):
def format(self, record):
log_record = {
'time': self.formatTime(record),
'name': record.name,
'level': record.levelname,
'hostname': getattr(record, 'hostname', ''),
'message': record.getMessage(),
'path': record.pathname,
'line': record.lineno
}
if record.exc_info:
log_record['exception'] = self.formatException(record.exc_info)
return json.dumps(log_record)
# ํธ๋ค๋ฌ ์ค์
handler = logging.FileHandler('app.log')
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
return logger
โ ํ๋ก๋์ ๋๋ฒ๊น ์ ๋ต:
- ๊ตฌ์กฐํ๋ ๋ก๊น
- ๋ถ์ฐ ํธ๋ ์ด์ฑ
- ๋ชจ๋ํฐ๋ง ๋๊ตฌ ํ์ฉ
- ์ค๋ฅ ๋ณด๊ณ ์์คํ ๊ตฌ์ถ
- ๋ณต๊ตฌ ๋ฉ์ปค๋์ฆ ๊ตฌํ
- A/B ํ ์คํธ ๋ฐ ์นด๋๋ฆฌ ๋ฐฐํฌ
ํจ์จ์ ์ธ ๋๋ฒ๊น ์ ์ํ ์ฃผ์ ํ๊ณผ ๋ชจ๋ฒ ์ฌ๋ก์ด๋ค.
โ ๋ชจ๋ฒ ์ฌ๋ก:
- ์ฒด๊ณ์ ์ธ ์ ๊ทผ๋ฒ: ๋ฌธ์ ๋ฅผ ๋ช ํํ ์ ์ํ๊ณ ๊ฐ์ค์ ์ธ์ฐ๊ณ ๊ฒ์ฆํ๋ ๊ณผํ์ ๋ฐฉ๋ฒ ์ฌ์ฉ
- ๋๋ฒ๊น ์ ๋ต ๋ค๋ณํ: ๋ค์ํ ๋๋ฒ๊น ๋๊ตฌ์ ๋ฐฉ๋ฒ์ ์ํฉ์ ๋ง๊ฒ ํ์ฉ
- ๋ก๊น ์ ๋ต ์๋ฆฝ: ์ผ๊ด๋ ๋ก๊น ์คํ์ผ๊ณผ ๋ ๋ฒจ ์ฌ์ฉ, ์ปจํ ์คํธ ์ ๋ณด ํฌํจ
- ๋๋ฒ๊ฑฐ ๋จ์ถํค ์์ง: ํจ์จ์ ์ธ ๋๋ฒ๊น ์ ์ํ ๋๊ตฌ ๋จ์ถํค ํ์ต
- ์กฐ๊ฑด๋ถ ๋ธ๋ ์ดํฌํฌ์ธํธ ํ์ฉ: ํน์ ์กฐ๊ฑด์์๋ง ์คํ์ ์ค๋จํ์ฌ ํจ์จ์ ์ผ๋ก ๋๋ฒ๊น
- ๋ณ์ ๊ฐ์ ๊ธฐ๋ฅ ํ์ฉ: ์ค์ ๋ณ์์ ์ํ ๋ณํ ๋ชจ๋ํฐ๋ง
- ์คํ ํธ๋ ์ด์ค ๋ถ์: ์ค๋ฅ ๋ฐ์ ๊ฒฝ๋ก์ ์ปจํ ์คํธ ์ดํด
- ๋ก๊ทธ ๋ ๋ฒจ ์ ์ ํ ์ฌ์ฉ: ์ํฉ์ ๋ง๋ ๋ก๊ทธ ๋ ๋ฒจ ์ ํ
- ํ๋ก๋์ ํ๊ฒฝ ๊ณ ๋ ค: ์ฑ๋ฅ๊ณผ ๋ณด์์ ๊ณ ๋ คํ ๋๋ฒ๊น ์ ๋ต ์๋ฆฝ
- ์ฑ๋ฅ ์ํฅ ๊ณ ๋ ค: ๋๋ฒ๊น ์ฝ๋๊ฐ ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ํฅ ์ธ์
โ ๋๋ฒ๊น ์ ์ฐจ:
- ๋ฌธ์ ์ฌํ: ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ์ ํํ ์กฐ๊ฑด ํ์
- ์ฆ์ ๋ถ์: ์ค๋ฅ ๋ฉ์์ง, ๋ก๊ทธ, ์คํ ํธ๋ ์ด์ค ๊ฒํ
- ๊ฐ์ค ์๋ฆฝ: ๊ฐ๋ฅํ ์์ธ์ ๋ํ ๊ฐ์ค ์ธ์ฐ๊ธฐ
- ๊ฐ์ค ๊ฒ์ฆ: ๋๋ฒ๊น ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ค ํ ์คํธ
- ํด๊ฒฐ์ฑ ๊ตฌํ: ๋ฌธ์ ํด๊ฒฐ ๋ฐ ์์
- ํ ์คํธ: ์์ ์ฌํญ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋์ง ํ์ธ
- ๋ฌธ์ํ: ๋ฐ๊ฒฌํ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ ๊ธฐ๋ก
๋ณต์กํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๊ณ ๊ธ ๋๋ฒ๊น ๊ธฐ๋ฒ์ด๋ค.
# ๋ชจ์ ๊ฐ์ฒด(Mock)๋ฅผ ์ฌ์ฉํ ๋๋ฒ๊น
from unittest.mock import patch, MagicMock
def test_with_mock():
# ์ธ๋ถ API ํธ์ถ์ ๋ชจ์ ๊ฐ์ฒด๋ก ๋์ฒด
with patch('module.api_call') as mock_api:
# ๋ชจ์ ๋ฐํ๊ฐ ์ค์
mock_api.return_value = {'status': 'success', 'data': [1, 2, 3]}
# ํ
์คํธ ๋์ ํจ์ ํธ์ถ
result = process_api_data()
# ํธ์ถ ๊ฒ์ฆ
mock_api.assert_called_once()
print(f"API ํธ์ถ ์ธ์: {mock_api.call_args}")
# ๊ฒฐ๊ณผ ๊ฒ์ฆ
assert result == [2, 4, 6]
# ๋๋ฒ๊น
์ ์ํ ๋ฐ์ฝ๋ ์ดํฐ
def debug_function_calls(func):
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"ํจ์ ํธ์ถ: {func.__name__}({signature})")
try:
result = func(*args, **kwargs)
print(f"ํจ์ ๋ฐํ: {func.__name__} -> {result!r}")
return result
except Exception as e:
print(f"ํจ์ ์์ธ: {func.__name__} -> {type(e).__name__}: {e}")
raise
return wrapper
@debug_function_calls
def complex_calculation(x, y, z=1):
return (x * y) / z
โ ๊ณ ๊ธ ๋๋ฒ๊น ์ ๋ต:
- ์ด์ง ๊ฒ์ ๋๋ฒ๊น : ์ค๋ฅ ์์ธ์ ๋น ๋ฅด๊ฒ ์ฐพ๊ธฐ ์ํด ์ฝ๋๋ฅผ ์ ๋ฐ์ฉ ๋๋์ด ๊ฒ์ฌ
- ๋ฌ๋ฒ๋ ๋๋ฒ๊น : ๋ฌธ์ ๋ฅผ ๋ค๋ฅธ ์ฌ๋(๋๋ ๋ฌผ์ฒด)์๊ฒ ์ค๋ช ํ๋ฉด์ ํด๊ฒฐ์ฑ ๋ฐ๊ฒฌ
- ๋์ ์ฝ๋ ์ฝ์ : ๋ฐํ์์ ์ฝ๋ ์์ ํ์ฌ ๋๋ฒ๊น
- ํ๋ ์ค์ฌ ๋๋ฒ๊น : ์ฝ๋๊ฐ "๋ฌด์์ ํ๋์ง"๊ฐ ์๋ "์ด๋ป๊ฒ ๋์ํ๋์ง" ์ง์ค
- ๊ฑฐ๊พธ๋ก ๋๋ฒ๊น : ์ค๋ฅ ๋ฐ์ ์ง์ ์์ ์ญ๋ฐฉํฅ์ผ๋ก ์ถ์
- ๋น๊ต ๋๋ฒ๊น : ์๋ํ๋ ๋ฒ์ ๊ณผ ์ค๋ฅ๊ฐ ์๋ ๋ฒ์ ๋น๊ต
ํ์ด์ฌ ๋ฒ์ ์ ๋ฐ๋ฅธ ์๋ก์ด ๋๋ฒ๊น ๊ธฐ๋ฅ๊ณผ ๋๊ตฌ๋ค์ด๋ค.
โ Python 3.7+:
-
breakpoint()
๋ด์ฅ ํจ์:pdb.set_trace()
์ ๋์ฒด -
PYTHONBREAKPOINT
ํ๊ฒฝ ๋ณ์๋ก ๋๋ฒ๊ฑฐ ์ ํ
โ Python 3.8+:
- ๋๋ฒ๊ทธ์ฉ f-strings:
f"{variable=}"
๋ก ๋ณ์๋ช ๊ณผ ๊ฐ ํจ๊ป ์ถ๋ ฅ - ํฅ์๋ ์๋ฌ ๋ฉ์์ง์ ์คํ ํธ๋ ์ด์ค
โ Python 3.9+:<
- ํฅ์๋ ํ์ ํํธ ์ง์์ผ๋ก ํ์ ๊ด๋ จ ๋๋ฒ๊น ๊ฐ์
- ๋ณด๋ค ์ ํํ ์ค ๋ฒํธ ์ถ์
โ Python 3.10+:
- ๋ ์์ธํ ๋ฌธ๋ฒ ์ค๋ฅ ๋ฉ์์ง์ ์ ์
- ๊ตฌ์กฐ์ ํจํด ๋งค์นญ์ ํ์ฉํ ์ค๋ฅ ์ฒ๋ฆฌ
โ Python 3.11+:
- ํฅ์๋ ํธ๋ ์ด์ค๋ฐฑ: ์ค๋ฅ ๋ฐ์ ์์น๋ฅผ ๋ ์ ํํ ํ์
- ์์ธ ๋
ธํธ:
add_note()
๋ฉ์๋๋ก ์์ธ์ ์ปจํ ์คํธ ์ ๋ณด ์ถ๊ฐ - ๋๋ฒ๊น ์ฑ๋ฅ ํฅ์
์ค์ ๋๋ฒ๊น ์ฐ์ต ์ฌ๋ก๋ฅผ ํตํด ๊ธฐ์ ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ด๋ค.
โ ์ฐ์ต์ฉ ๋ฒ๊ทธ ์ ํ:
- ๋ ผ๋ฆฌ ์ค๋ฅ: ์๊ณ ๋ฆฌ์ฆ์ด๋ ์กฐ๊ฑด๋ฌธ ์ค๋ฅ
- ํ์ ์ค๋ฅ: ์์์น ๋ชปํ ํ์ ๋ณํ
- ๋ฒ์ ์ค๋ฅ: ๋ฐฐ์ด ์ธ๋ฑ์ค ์ด๊ณผ
- ๊ฒฝ์ ์กฐ๊ฑด: ๋ณ๋ ฌ ์ฝ๋์ ํ์ด๋ฐ ๋ฌธ์
- ๋ฉ๋ชจ๋ฆฌ ๋์: ์์์ด ํด์ ๋์ง ์๋ ๋ฌธ์
- ๋ฌดํ ๋ฃจํ: ์ข ๋ฃ ์กฐ๊ฑด์ด ์๋ ๋ฐ๋ณต๋ฌธ
โ ์ฐ์ต ํ๋ก์ ํธ:
- ์๋์ ์ผ๋ก ๋ฒ๊ทธ๊ฐ ์๋ ์ฝ๋๋ฅผ ๋ง๋ค๊ณ ํด๊ฒฐํ๊ธฐ
- ์คํ ์์ค ํ๋ก์ ํธ์ ์ด์ ํด๊ฒฐ์ ์ฐธ์ฌํ๊ธฐ
- ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ๋ค๋ฅธ ์ฌ๋์ ์ฝ๋ ๋๋ฒ๊น ํ๊ธฐ
- ๋๋ฒ๊น ๋๊ตฌ์ ์๋ก์ด ๊ธฐ๋ฅ ํ์ตํ๊ธฐ
- ์ฑ๋ฅ ๋ณ๋ชฉ ํ์ ์ฐพ๊ณ ์ต์ ํํ๊ธฐ
ํจ๊ณผ์ ์ธ ๋๋ฒ๊น
์ ํ์ด์ฌ ๊ฐ๋ฐ์ ํต์ฌ ๊ธฐ์ ์ด๋ค. ๊ธฐ๋ณธ์ ์ธ print ๋ฌธ๋ถํฐ ๊ณ ๊ธ ํ๋กํ์ผ๋ง ๋๊ตฌ๊น์ง ๋ค์ํ ๋ฐฉ๋ฒ์ ์ํฉ์ ๋ง๊ฒ ํ์ฉํ๋ ๊ฒ์ด ์ค์ํ๋ค. ๋๋ฒ๊น
์ ๋จ์ํ ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ ๊ฒ์ด ์๋๋ผ, ์ฝ๋์ ์๋ ๋ฐฉ์์ ์ดํดํ๊ณ ๊ฐ์ ํ๋ ๊ณผ์ ์ด๋ค. ์ฒด๊ณ์ ์ธ ์ ๊ทผ๋ฒ๊ณผ ์ ์ ํ ๋๊ตฌ๋ฅผ ํ์ฉํ๋ฉด ๋ณต์กํ ๋ฌธ์ ๋ ํจ์จ์ ์ผ๋ก ํด๊ฒฐํ ์ ์๋ค.
๋ฌธ์ ํด๊ฒฐ ๊ณผ์ ์์ ๋ฐฐ์ด ๊ตํ์ ๋ฌธ์ํํ๊ณ ํ๊ณผ ๊ณต์ ํ๋ ๊ฒ๋ ์ค์ํ๋ค. ๋๋ฒ๊น
์ ๊ธฐ์ ์ ์ธ ์ธก๋ฉด๋ฟ๋ง ์๋๋ผ ์ธ๋ด์ฌ๊ณผ ๋
ผ๋ฆฌ์ ์ฌ๊ณ ๋ฅผ ์๊ตฌํ๋ ๊ธฐ์ ์ด๋ฏ๋ก, ์ง์์ ์ธ ์ฐ์ต๊ณผ ํ์ต์ ํตํด ํฅ์์์ผ์ผ ํ๋ค.