Python 데코레이터(decorator) 완전 가이드
이 글은 Python에서 자주 사용되는 데코레이터(decorator)를 이해하고, 직접 만들어서 활용할 수 있도록 개념부터 실전 예제까지 정리한 가이드입니다.
1. 데코레이터란 무엇인가?
Python에서 데코레이터는 “함수나 메서드를 감싸서, 공통 동작을 추가해 주는 문법”입니다.
함수 정의 위에 @something 형태로 붙어있는 코드를 많이 보게 됩니다.
예를 들어:
@login_required
def my_page():
...
이런 코드는 실제로 my_page = login_required(my_page) 로 치환됩니다.
즉, my_page 함수는 이제 login_required가 감싼 새로운 함수가 됩니다.
2. 가장 단순한 데코레이터 만들어보기
“함수 시작/끝”을 출력하는 간단한 데코레이터를 만들어 봅시다.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("➡ 함수 시작")
result = func(*args, **kwargs)
print("⬅ 함수 끝")
return result
return wrapper
@my_decorator
def hello():
print("안녕!")
hello()
실행 순서는 다음과 같습니다.
@my_decorator→hello = my_decorator(hello)로 대체hello()를 호출하면 실제로는wrapper()실행wrapper안에서 공통 동작 + 원래 함수(func) 호출
3. 왜 *args, **kwargs 를 쓸까?
데코레이터는 인자가 없는 함수, 인자가 여러 개인 함수, 키워드 인자를 쓰는 함수 등 다양한 형태를 모두 감쌀 수 있어야 합니다.
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
이렇게 정의해 두면 어떤 시그니처의 함수든 동일한 데코레이터를 재사용할 수 있습니다.
4. @wraps를 써야 하는 이유 (functools.wraps)
위에서 만든 데코레이터를 그대로 사용하면, hello.__name__이
"wrapper"로 나오는 문제점이 있습니다.
함수 이름, docstring 등 메타데이터를 보존하려면
functools.wraps를 사용하는 것이 좋습니다.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("➡ 함수 시작")
result = func(*args, **kwargs)
print("⬅ 함수 끝")
return result
return wrapper
이렇게 하면 hello.__name__, docstring 등의 정보가
원래 함수의 것을 그대로 유지합니다.
5. 인자를 받는 데코레이터
데코레이터 자체에도 옵션을 주고 싶다면, “데코레이터를 만드는 함수”를 한 번 더 감싸야 합니다.
from functools import wraps
def log(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] {func.__name__} 시작")
result = func(*args, **kwargs)
print(f"[{level}] {func.__name__} 끝")
return result
return wrapper
return decorator
@log("INFO")
def add(a, b):
return a + b
add(3, 4)
@log("INFO") 를 쓰면 log("INFO")가 먼저 실행되어 데코레이터를 만들고,
그 데코레이터가 다시 원래 함수를 감싸는 구조입니다.
6. 자주 쓰이는 내장 데코레이터
6-1. @staticmethod
인스턴스와 무관한 유틸리티 메서드를 클래스 내부에 정의할 때 사용합니다.
class MathUtil:
@staticmethod
def add(a, b):
return a + b
MathUtil.add(1, 2)
6-2. @classmethod
첫 번째 인자로 cls를 받아, 클래스 자체를 다루는 메서드를 만들 때 사용합니다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_string(cls, text):
name, age = text.split(",")
return cls(name, int(age))
p = Person.from_string("Alice,30")
6-3. @property
메서드를 “속성”처럼 사용할 수 있게 해주는 데코레이터입니다.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
rect = Rectangle(3, 4)
print(rect.area) # 함수 호출이 아니라 속성 접근처럼 사용
6-4. @lru_cache
같은 인자로 반복 호출되는 함수의 결과를 캐싱하여 성능을 개선할 때 사용합니다.
from functools import lru_cache
@lru_cache(maxsize=128)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
fib(30)
6-5. @dataclass
데이터를 담는 클래스를 간단하게 정의할 수 있게 해줍니다.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
u = User("Alice", 30)
print(u)
7. 실전 패턴 예제
7-1. 실행 시간 측정 데코레이터
import time
from functools import wraps
def timeit(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 실행 시간: {end - start:.4f}초")
return result
return wrapper
@timeit
def slow_func():
time.sleep(1)
slow_func()
7-2. 로그인 체크 / 권한 체크
from functools import wraps
def login_required(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get("is_authenticated"):
print("로그인이 필요합니다.")
return None
return func(user, *args, **kwargs)
return wrapper
@login_required
def my_page(user):
print(f"{user['name']}님의 마이페이지입니다.")
my_page({"name": "홍길동", "is_authenticated": True})
8. 데코레이터 여러 개 동시에 사용하기
@dec1
@dec2
def func():
...
적용 순서는:
func = dec1(dec2(func))
아래에 있는 데코레이터가 먼저 적용된다는 점을 기억해 두면 좋습니다.
9. 데코레이터 사용 시 주의할 점
@wraps를 써서 원래 함수의 이름/메타데이터를 보존하기- 너무 많은 책임을 데코레이터 한 곳에 몰지 않기 (가독성 저하)
- 상태를 가지는 복잡한 데코레이터는 클래스나 클로저 설계를 신중히 하기
'프로그래밍 > Python' 카테고리의 다른 글
| [Python] lru_cache는 언제 사용해? (0) | 2025.12.15 |
|---|---|
| [Python] @property는 언제 사용해? (0) | 2025.12.15 |
| [Python] classmethod는 언제 사용해? (0) | 2025.12.15 |
| [Python] staticmethod는 언제 사용해? (0) | 2025.12.15 |
| [Python] decorator에서 wrapper 자세히 보기 (0) | 2025.12.15 |