🎀
Decorator 내부 구조 — @가 실제로 하는 일
함수를 받아서 함수를 반환하는 함수 — 그게 전부다
@my_decorator
def greet():
return 'hello'
# 위 코드는 이것과 완전히 동일하다:
def greet():
return 'hello'
greet = my_decorator(greet)
@는 특별한 마법이 아니다. 함수를 인자로 받아서 새 함수를 반환하는 함수를 적용하는 문법적 편의일 뿐.
기본 구조
def my_decorator(func):
def wrapper(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return wrapper
wrapper가 클로저다. 외부 함수(my_decorator)의 변수(func)를 캡처해서 보존한다. *args, **kwargs로 원본 함수가 어떤 시그니처든 받을 수 있게 한다.
functools.wraps가 왜 필요한가
decorator를 적용하면 greet.__name__이 'wrapper'가 된다. 원본 함수의 메타데이터(이름, docstring, 시그니처)가 사라진다. @functools.wraps(func)를 wrapper에 달면 원본 메타데이터를 복사해준다.
인자가 있는 decorator
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # repeat(3)이 먼저 실행 → decorator 반환 → decorator(func) 실행
def say_hello():
print('hello')
3중 중첩이 된다. repeat(3)이 decorator를 반환하고, decorator(say_hello)가 wrapper를 반환한다. FastAPI의 @app.get('/path')도 이 패턴.
클래스 decorator
__call__ 메서드가 있는 클래스도 decorator로 쓸 수 있다. 상태를 유지해야 하는 경우(호출 횟수 카운팅 등)에 유용하다.
핵심 포인트
1
@decorator는 func = decorator(func)의 문법적 설탕
2
wrapper 함수가 클로저로 원본 func을 캡처
3
functools.wraps(func)로 원본 메타데이터 보존 필수
4
인자 있는 decorator는 3중 중첩: outer(args) → decorator(func) → wrapper(*args)
사용 사례
로깅 — 함수 호출 시점/인자/반환값 자동 기록
FastAPI — @app.get("/path")로 라우팅 등록