본문 바로가기
👩🏻‍💻 Computer Science/clean-code(ft.python)

Chapter5. 데코레이터 활용 사례(파라미터 변환, 유효성 검사, 코드추적)

by devhertz 2026. 9. 27.

데코레이터 활용 우수 사례

데코레이터를 언제, 어떤 목적으로 쓰면 좋은지 데코레이터가 특히 유용한 대표적인 상황들을 소개한다.

 

데코레이터가 좋은 선택이 되는 주요 경우
활용 사례 설명 효과
파라미터 변환 • 복잡하고 반복적인 인자를 받는 레거시 함수나 프레임워크 함수에서, 데코레이터가 중간에서 인자를 가로채 가공한 뒤 깔끔한 도메인 객체(helper)로 변환해줌
• 파라미터가 어떻게 처리되는지 세부사항을 숨기면서 함수의 서명을 변경함
• 복잡한 함수에 더 깔끔한 인터페이스를 제공할 때 유용. 단, 의도를 명확히 해야 함
• 대규모 리팩토링 없이도 함수의 서명을 더 간단하고 직관적으로 변경할 수 있으며, 디자인 패턴에서의 어댑터(Adapter) 역할을 수행
코드 추적 (Tracing) • 기존 코드를 전혀 수정하지 않고 외부에서 함수의 실행 경로와 상태를 모니터링하는 강력한 추상화 도구 • 함수의 실행 경로 추적 (실행 함수 로깅)
• 시스템 지표 모니터링 (CPU, 메모리 사용량 등)
• 함수의 실행 시간 측정
• 실행 시점 및 전달된 파라미터 로깅
파라미터 유효성 검사 • 함수의 인자 값이나 데이터 타입이 올바른지 투명하게 검사하는 데 사용됨 • DbC(계약에 의한 디자인) 원칙에 따라 함수의 사전조건(Pre-condition)이나 사후조건(Post-condition)을 강제할 수 있어 코드의 안정성이 높아짐
재시도 로직 구현 • retry 데코레이터처럼 구현 • 네트워크 불안정이나 일시적인 서버 오류로 실패하는 작업에 대해, 데코레이터를 이용해 자동으로 재시도하는 로직을 손쉽게 붙일 수 있음
반복 작업을 데코레이터로 이동 • 클래스나 함수에서 반복되는 작업을 데코레이터로 빼내어 단순화 • DRY 원칙과 직접 관련

 

함수 서명 변경(파라미터 변환)

  • 객체지향에서는 서로 다른 인터페이스를 가진 객체를 연결할 때 어댑터 패턴을 사용함
  • 이와 비슷하게, 함수의 서명(파라미터 구조)을 변경하고 싶을 때 데코레이터를 사용할 수 있음
  • 왜 필요한가?
    • 레거시 코드나 프레임워크에는 복잡하고 긴 파라미터를 가진 함수가 많음

예시) 이 함수를 여러 곳에서 호출하다 보면, 매번 같은 보일러플레이트 코드가 반복되고 가독성이 떨어짐

def resolver_function(root, args, context, info):
    helper = DomainObject(root, args, context, info)
    ...
    helper.process()
  • 데코레이터를 사용해서 복잡한 파라미터를 숨기고, 도메인 객체만 받도록 서명을 단순화할 수 있음
@DomainArgs
def resolver_function(helper):
    helper.process()
  • 동작
    • 데코레이터가 원래의 (root, args, context, info)를 가로챈다.
    • 내부에서 DomainObject를 생성한다.
    • 데코레이팅된 함수에는 이미 만들어진 helper 객체만 전달한다.
    • 반대로, 기존 코드가 객체를 받아서 여러 파라미터로 분해해 사용하는 경우에도 데코레이터를 중간 레이어로 사용해 변경을 최소화할 수 있다.

데코레이터를 사용해서 함수를 더 간단하고 간결한 서명을 갖도록 만들자

  • 주의할 점
    • 명확한 의도를 가지고 사용할 때만 유용함
    • 실수로 함수 서명을 이상하게 바꿔버리면 오히려 코드가 혼란스러워짐

 

파라미터 유효성 검사

  • 데코레이터는 파라미터의 값이나 타입이 유효한지 검사하는 데 매우 적합
  • Design by Contract(DbC) 원칙에 따라 사전조건(precondition) 이나 사후조건(postcondition) 을 강제할 수 있음
  • 비슷한 검증 로직이 여러 함수에 반복될 때, 데코레이터로 한 곳에 모아두면 DRY 원칙을 지킬 수 있음

특히 다음과 같은 상황에서 자주 사용됨

  • 필수 값이 있는지 검사
  • 타입이 맞는지 검사
  • 값의 범위가 유효한지 검사
  • 특정 조건을 만족하는지 검사

데코레이터를 사용하면 검증 로직과 핵심 비즈니스 로직을 분리할 수 있어서
코드가 훨씬 깔끔해진다.

 

코드 추적

  • 여기서 말하는 추적(tracing) 은 함수의 실행과 관련된 모니터링을 의미함
  • 대표적인 활용 목적
    • 함수의 실행 경로 추적 (어떤 함수가 호출되었는지 로깅)
    • 함수 지표 모니터링 (CPU 사용량, 메모리 사용량 등)
    • 함수의 실행 시간 측정
    • 언제 함수가 실행되었고, 어떤 파라미터가 전달되었는지 로깅

이런 기능들은 대부분 데코레이터 형태로 제공되는 경우가 많음. 기존 비즈니스 코드를 전혀 수정하지 않고도 외부에서 기능을 붙일 수 있기 때문에 강력한 추상화 수단이 된다.

 

예시) 파라미터 유효성 검사 + 코드 추적 데코레이터

import time
from functools import wraps

# 1. 파라미터 유효성 검사 데코레이터
def validate_params(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not args:
            raise ValueError("인자가 필요합니다")
        
        value = args[0]
        
        if not isinstance(value, int):
            raise TypeError("첫 번째 인자는 정수여야 합니다")
        
        if value <= 0:
            raise ValueError("첫 번째 인자는 0보다 커야 합니다")
        
        print(f"✅ 유효성 검사 통과: {value}")
        return func(*args, **kwargs)
    
    return wrapper


# 2. 코드 추적 데코레이터
def trace(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"🚀 [{func.__name__}] 실행 시작")
        print(f"   전달된 인자: args={args}, kwargs={kwargs}")
        
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        
        print(f"✅ [{func.__name__}] 실행 완료")
        print(f"   반환값: {result}")
        print(f"   실행 시간: {end - start:.4f}초")
        print("-" * 40)
        
        return result
    
    return wrapper


# 3. 두 데코레이터를 함께 사용 (아래에서 위로 적용됨)
@trace
@validate_params
def process_number(n):
    time.sleep(0.2)  # 시간이 좀 걸리는 척
    return n * 10


# 테스트
print(process_number(5))
# print(process_number(-3))
# print(process_number("abc"))

 

실행 결과)

🚀 [process_number] 실행 시작
   전달된 인자: args=(5,), kwargs={}
✅ 유효성 검사 통과: 5
✅ [process_number] 실행 완료
   반환값: 50
   실행 시간: 0.2015초
----------------------------------------
50

 

참고) 적용 순서

@trace
@validate_params
def process_number(n):
    ...
  • process_number = trace(validate_params(process_number))
  • 이 경우 먼저 유효성 검사를 하고 → 그 다음에 추적(로깅/시간측정)이 실행됨. 순서를 바꾸고 싶으면 데코레이터 위치를 서로 바꾸면 된다.

 

📌 정리: 데코레이터는 “핵심 로직은 그대로 두고, 부가적인 관심사(검증, 로깅, 변환, 재시도 등)를 바깥에서 붙이는” 용도로 쓸 때 가장 효율적이다.
활용 목적 데코레이터의 역할 얻을 수 있는 이점
함수 서명 변경 복잡한 파라미터를 숨기고 간단한 인터페이스 제공 가독성 ↑, 레거시/프레임워크 코드와의 결합 최소화
파라미터 검증 유효성 검사를 투명하게 적용 관심사 분리, DbC 원칙 적용 가능
코드 추적 로깅, 시간 측정, 모니터링 기존 코드 수정 없이 기능 추가
재시도 / 반복 작업 횡단 관심사를 데코레이터로 이동 DRY 원칙 준수, 클래스/함수 단순화

댓글