파이썬의 데코레이터(decorator)는 기존 함수를 수정하지 않고도 추가적인 기능을 손쉽게 덧붙일 수 있는 강력한 도구이다.
데코레이터의 기본 개념과 활용하는 법을 코드를 통해 알아보자.
데코레이터란?
데코레이터는 하나의 함수를 입력받아 새로운 함수를 반환하는 함수이다.
이 방식을 통해 로깅, 실행 시간 측정, 캐싱 등 공통적인 기능을 여러 함수에 쉽게 적용할 수 있다.
핵심 로직과 부가 기능을 분리하여 코드의 재사용성과 가독성을 높일 수 있다는 점이 큰 장점이다.
데코레이터의 장점 및 필요성
- 코드 재사용성 향상
여러 함수에 동일한 기능을 중복 없이 적용할 수 있다. - 관심사의 분리
주요 기능과 부가 기능(예: 인증, 로깅)을 분리하여 코드의 가독성 및 유지보수가 용이하다. - 유연한 기능 추가
함수 실행 전후에 다양한 부가 작업을 쉽게 삽입할 수 있다.
데코레이터의 활용
1. 데코레이터 기본 구조
데코레이터는 보통 함수를 인자로 받아 새로운 함수를 반환하는 함수이다.
즉, 다음과 같이 정의한다:
def my_decorator(func):
def wrapper(*args, **kwargs):
# 함수 실행 전 처리할 작업
print("함수 실행 전 작업")
result = func(*args, **kwargs)
# 함수 실행 후 처리할 작업
print("함수 실행 후 작업")
return result
return wrapper
여기서 wrapper
함수는 원래 함수(func
)를 호출하기 전과 후에 추가 작업을 수행한다.
2. 데코레이터 적용 방법
a. 문법 사용
가장 일반적인 방법은 @데코레이터_이름
을 사용하여 데코레이터를 함수 위에 명시하는 것이다.
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
이렇게 하면 say_hello
함수가 호출될 때 실제로는 my_decorator(say_hello)
에 의해 반환된 wrapper
함수가 실행된다.

b. 직접 적용하기
데코레이터는 단순히 함수를 감싸는 함수이므로 다음과 같이 직접 적용할 수도 있다.
def say_hello(name):
print(f"Hello, {name}!")
decorated_function = my_decorator(say_hello)
decorated_function("Bob")

3. 인자와 키워드 인자 전달
데코레이터를 정의할 때 *args
와 **kargs
를 사용하면 원래 함수가 받는 인자들을 그대로 전달할 수 있어 어떤 형태의 함수든 데코레이터를 적용할 수 있다.
def logging_decorator(func):
def wrapper(*args, **kwargs):
print(f"{func.__name__} 호출됨: args={args}, kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@logging_decorator
def add(a, b):
return a + b
print(add(3, 5))

4. 함수 메타데이터 보존
데코레이터를 사용하면 원래 함수의 이름, 문자열 등 메타데이터가 wrapper
함수로 덮어씌워질 수 있다.
이를 방지하기 위해 functools.wraps
를 사용한다.
import functools
def my_decorator(func):
@functools.wraps(func) # 원래 함수의 메타데이터를 복사
def wrapper(*args, **kwargs):
print("실행 전 작업")
result = func(*args, **kwargs)
print("실행 후 작업")
return result
return wrapper
@my_decorator
def greet(name):
"""이 함수는 인사를 합니다."""
print(f"안녕하세요, {name}님!")
greet("Charlie")
print(greet.__name__) # greet
print(greet.__doc__) # 이 함수는 인사를 합니다.
@functools.wraps(func)
를 사용하면, greet
함수의 원래 이름, 문서 문자열 등이 유지된다.

5. 여러 데코레이터 사용
하나의 함수에 여러 데코레이터를 적용할 수 있다.
적용 순서는 아래와 같이 가장 아래에 있는 데코레이터가 먼저 적용된다.
def decorator_one(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Decorator One - 시작")
result = func(*args, **kwargs)
print("Decorator One - 종료")
return result
return wrapper
def decorator_two(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Decorator Two - 시작")
result = func(*args, **kwargs)
print("Decorator Two - 종료")
return result
return wrapper
@decorator_one
@decorator_two
def multiply(a, b):
return a * b
print(multiply(4, 5))
이 경우, multiply
함수는 먼저 decorator_two
로 감싸진 후, 다시 decorator_one
으로 감싸지게 된다.
이 코드를 실행 시,
decorator_one
시작decorator_two
시작- 함수 실행
decorator_two
종료decorator_one
종료
순서로 돌아간다.
아래 예시를 통해 살펴보자.

위의 설명에 따르면 one 시작, two 시작, 20, two 종료, one 종료가 되어야 한다고 생각할 수 있다.
하지만 20이 가장 나중에 출력되는 것을 확인할 수 있는데, 20이 중간에 출력되지 않는 이유는 다음과 같다.
- 20은
multiply
함수의 반환값으로, 데코레이터 내부에서 리턴(return) 되기만 할 뿐, 내부에서 바로 출력되지 않는다. - 최종적으로 20이 출력되는 시점은 모든 데코레이터(wrapper) 함수들이 종료되고, 최종적으로
print()
함수가 반환값을 출력할 때이다.
즉, 각 데코레이터는 함수 호출 전과 후에 자신의 작업(출력)을 수행할 뿐, 함수의 반환값을 출력하는 역할은 외부의 print()
문이 담당하기 때문에 가장 나중에 값이 출력된다.
아무튼 맨 마지막에 실행되는 것이 print()
문이기 때문에 20이 가장 나중에 출력되는 것이다.
설명을 들으면 당연한 것이지만 내가 착각에 빠져 디버깅하는데 힘들었다. 그래서 작성하는 포스팅
순서에 헷갈리지 않도록 하자.
'머신 러닝 > Python' 카테고리의 다른 글
[Python] 파이썬 예외 처리(Exception Handling)와 커스텀 예외 만들기 (0) | 2025.02.05 |
---|---|
[Python] matplotlib으로 서브플롯 그리고 각각에 타이틀 추가하기 (0) | 2025.01.27 |
[Python] npz 파일 키 확인하는 법 (0) | 2024.08.04 |
[Python] 파이썬에서 숫자에 콤마(,)찍어서 출력하는 방법 (0) | 2024.07.14 |
[Python] for문을 사용하지 않은 List Comprehension 예제 (1) | 2024.07.12 |