2025.8.22: 케이스별 데코레이터 정리

jiyongg·2025년 8월 22일

TIL: Today I Learned

목록 보기
28/30

오늘은 테스트 코드를 작성하는 작업을 했는데, 테스트 코드를 작성하는 과정에서 데코레이터를 활용해서 테스트 결과를 출력하는 코드를 짜려고 했다.

그런데, 데코레이터에 대해서 좀 헷갈리는 부분이 있었다. 데코레이터의 사용법 자체는 그리 헷갈리진 않았는데, 데코레이터 함수의 선언 방법이 좀 헷갈렸다.

그래서, 이번 기회에 데코레이터를 정리해 보았다. 헷갈리는 부분을 정리해서 4개의 케이스로 나누고, 약간의 팁을 덧붙여봤다.

데코레이터의 가능한 경우의 수

함수의 매개변수 없음함수의 매개변수 있음
데코레이터의 매개변수 없음케이스 ①케이스 ②
데코레이터의 매개변수 있음케이스 ③케이스 ④

케이스 ①

1) 데코레이터 선언

def decorator(func):
    def wrapper():
        func()
        # 데코레이터 로직
        
    return wrapper

2) 함수 선언에서 사용

@decorator
def func():
    pass

아래와 같은 의미를 가지게 됨

func = decorator(func)

구조 해석

  • decorator(func): decorator 함수를 func 인자 전달하여 호출
  • decorator 함수가 wrapper를 반환, func = wrapper
  • wrapper 함수에서 func 호출 (func()) 및 데코레이터 로직 수행

케이스 ②

1) 데코레이터 선언

def decorator(func):
    def wrapper(*func_args, **func_kwargs):
        func(*func_args, **func_kwargs)
        # 데코레이터 로직
        
    return wrapper

2) 함수 선언에서 사용

@decorator
def func(*func_args, **func_kwargs):
    pass

데코레이터로 만들어진 함수는 아래와 같은 의미를 가지게 됨

func = decorator(func)

구조 해석

  • decorator(func): decorator 함수를 func 인자 전달하여 호출
  • decorator 함수가 wrapper를 반환, func = wrapper
  • wrapper 함수에서 *func_args, **func_kwargs 인자 전달하여 func 호출 (func(*func_args, **func_kwargs)) 및 데코레이터 로직 수행

케이스 ③

1) 데코레이터 선언

def decorator_factory(*deco_args, **deco_kwargs):
    def decorator(func):
        def wrapper():
            func()
            # 데코레이터 로직
            
        return wrapper
    return decorator

2) 함수 선언에서 사용

@decorator_factory(*deco_args, **deco_kwargs)
def func():
    pass

데코레이터로 만들어진 함수는 아래와 같은 의미를 가지게 됨

func = decorator_factory(*deco_args, **deco_kwargs)(func)

구조 해석

  • decorator_factory(*deco_args, **deco_kwargs): decorator_factory 함수를 *deco_args, **deco_kwargs 인자 전달하여 호출
  • decorator_factory 함수가 decorator를 반환, func = decorator(func)
  • decorator(func): decorator 함수를 func 인자 전달하여 호출
  • decorator 함수가 wrapper를 반환, func = wrapper
  • wrapper 함수에서 func 호출 (func()) 및 데코레이터 로직 수행

케이스 ④

1) 데코레이터 선언

def decorator_factory(*deco_args, **deco_kwargs):
    def decorator(func):
        def wrapper(*func_args, **func_kwargs):
            func(*func_args, **func_kwargs)
            # 데코레이터 로직
            
        return wrapper
    return decorator

2) 함수 선언에서 사용

@decorator_factory(*deco_args, **deco_kwargs)
def func(*func_args, **func_kwargs):
    pass

데코레이터로 만들어진 함수는 아래와 같은 의미를 가지게 됨

func = decorator_factory(*deco_args, **deco_kwargs)(func)

구조 해석

  • decorator_factory(*deco_args, **deco_kwargs): decorator_factory 함수를 *deco_args, **deco_kwargs 인자 전달하여 호출
  • decorator_factory 함수가 decorator를 반환, func = decorator(func)
  • decorator(func): decorator 함수를 func 인자 전달하여 호출
  • decorator 함수가 wrapper를 반환, func = wrapper
  • wrapper 함수에서 *func_args, **func_kwargs 인자 전달하여 func 호출 (func(*func_args, **func_kwargs)) 및 데코레이터 로직 수행

예시: unittest 테스트 메소드에서 성공 메시지 출력

def print_test_success_message(test_description: str):
    def decorator(func):
        def wrapper(testcase_instance):
            func(testcase_instance)
            print(f"테스트 성공! 테스트 내용: {test_description}")
            
        return wrapper
    return decorator

class MyTestCase(unittest.TestCase):
    @print_test_success_message("something")
    def test_something(self):
        # 테스트 코드 작성

헷갈릴 때 팁

  • 데코레이터의 매개변수 유무에 따라 구조가 다름
    • 데코레이터의 매개변수가 없음: 2단 방식 (decorator -> wrapper)
      • func = decorator(func)
    • 데코레이터의 매개변수가 있음: 3단 방식 (decorator_factory -> decorator -> wrapper)
      • func = decorator_factory(*deco_args, **deco_kwargs)(func)
  • 매개변수에 대한 팁
    • decorator_factory: 데코레이터의 인자를 매개변수로 취함
    • decorator: 함수 객체(callable)를 매개변수로 취함
    • wrapper: 함수의 인자를 매개변수로 취함
profile
그냥 쓰고 싶은 것 쓰는 개발(?) 블로그

0개의 댓글