💻 프로그래밍

데코레이터

Decorator

함수나 클래스의 동작을 수정하는 래퍼. Python의 @decorator 문법. 메타프로그래밍에 활용.

📖 상세 설명

데코레이터는 함수나 클래스의 동작을 수정하거나 확장하는 디자인 패턴이자 문법입니다. Python에서는 @decorator 문법으로, JavaScript/TypeScript에서는 실험적 데코레이터 기능으로 널리 사용됩니다.

Python의 데코레이터는 본질적으로 함수를 인자로 받아 새 함수를 반환하는 고차 함수입니다. @log_time으로 함수를 꾸미면, 해당 함수 호출 전후에 시간을 측정하는 로직을 추가할 수 있습니다. 원본 함수 코드를 수정하지 않고도 기능을 확장하는 것입니다.

데코레이터는 횡단 관심사(cross-cutting concerns)를 처리하는 데 유용합니다. 로깅, 인증, 캐싱, 재시도 로직, 입력 검증 등 여러 함수에 공통으로 적용되는 기능을 데코레이터로 추출하면 코드 중복을 줄이고 관심사를 분리할 수 있습니다.

클래스 데코레이터는 클래스 전체를 감싸거나 수정합니다. Python의 @dataclass는 클래스에 __init__, __repr__ 등을 자동 생성합니다. 여러 데코레이터를 쌓아서 사용할 수 있으며, 아래에서 위로 적용됩니다.

💻 코드 예제

Python
import functools
import time

# 기본 데코레이터: 실행 시간 측정
def timer(func):
    @functools.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

# 인자를 받는 데코레이터
def retry(max_attempts=3):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"시도 {attempt + 1} 실패: {e}")
            raise Exception(f"{max_attempts}번 시도 모두 실패")
        return wrapper
    return decorator

# 데코레이터 적용
@timer
@retry(max_attempts=3)
def fetch_data(url):
    print(f"데이터 가져오는 중: {url}")
    return {"data": "결과"}

# 클래스 메서드 데코레이터
class API:
    @staticmethod
    def require_auth(func):
        @functools.wraps(func)
        def wrapper(self, *args, **kwargs):
            if not self.is_authenticated:
                raise PermissionError("인증 필요")
            return func(self, *args, **kwargs)
        return wrapper

# 사용
result = fetch_data("https://api.example.com")

🗣️ 실무 대화 예시

백엔드 개발자
"모든 API 엔드포인트에 인증 체크 코드가 중복되고 있어요. 데코레이터로 빼면 어떨까요?"
시니어 개발자
"좋아요. @require_auth 데코레이터를 만들면 각 핸들러에 한 줄만 추가하면 되니까 훨씬 깔끔해질 거예요."
백엔드 개발자
"권한 레벨별로 다른 데코레이터를 만들어서 @require_admin, @require_user로 구분하면 더 좋겠네요."
면접관
"Python에서 데코레이터가 어떻게 동작하는지 설명해주세요."
지원자
"데코레이터는 함수를 인자로 받아 새 함수를 반환하는 고차 함수입니다. @decorator는 func = decorator(func)와 동일합니다. 원본 함수를 감싸서 전처리나 후처리 로직을 추가합니다."
면접관
"functools.wraps를 사용하는 이유는 무엇인가요?"
지원자
"데코레이터로 감싸면 원본 함수의 __name__, __doc__ 등 메타데이터가 래퍼 함수의 것으로 대체됩니다. wraps를 사용하면 원본 함수의 메타데이터가 보존되어 디버깅과 문서화에 유리합니다."
리뷰어
"이 데코레이터에 functools.wraps가 빠져있네요. 추가하지 않으면 함수 이름이 wrapper로 보여서 디버깅이 어려워져요."
작성자
"아, 맞아요. 프로덕션 로그에서 함수명을 추적해야 하는데 중요한 부분이네요. 바로 추가하겠습니다."
리뷰어
"그리고 데코레이터 순서도 확인해주세요. @cache 다음에 @timer를 붙이면 캐시 히트 시에도 시간이 측정돼서 의미 없는 로그가 남아요."

⚠️ 주의사항

🔗 관련 용어

📚 더 배우기