
파이썬 데코레이터는 기존 함수의 본문을 직접 고치지 않고 공통 동작을 덧붙이는 문법입니다. 실행 시간 측정, 접근 권한 검사, 재시도, 캐시, 트랜잭션 처리처럼 여러 함수에서 반복되는 코드를 한곳에 모을 수 있습니다. 하지만 편리하다는 이유로 너무 많은 기능을 숨기면 함수 호출만 보고 실제 동작을 예측하기 어려워집니다. 실무에서는 횡단 관심사를 작고 명확하게 분리하고 원래 함수의 정보와 예외 의미를 보존하는 것이 중요합니다.
데코레이터가 동작하는 기본 원리
@trace를 함수 위에 붙이는 코드는 해당 함수를 trace(함수)의 반환값으로 교체하는 문법 설탕입니다. 데코레이터는 함수를 인자로 받고 새로운 호출 가능 객체를 반환합니다. 내부 wrapper는 위치 인자와 키워드 인자를 모두 전달하도록 *args와 **kwargs를 사용하고, 원래 함수의 반환값을 그대로 돌려줘야 합니다. 반환을 빠뜨리면 정상 함수도 None을 반환하게 됩니다.
functools.wraps를 반드시 사용하기
wrapper만 반환하면 함수 이름과 설명문, 타입 관련 메타데이터가 wrapper의 것으로 바뀝니다. 로그와 API 문서, 테스트 도구가 원래 함수 정보를 찾지 못할 수 있습니다. @functools.wraps(func)를 wrapper 위에 적용하면 주요 메타데이터와 원본 함수 참조가 보존됩니다. 디버깅과 프레임워크 연동을 위해 선택 사항이 아니라 기본 규칙으로 보는 편이 좋습니다.
기본 체크포인트
- 원래 인자를 변경 없이 전달합니다.
- 원래 반환값을 그대로 반환합니다.
- functools.wraps로 메타데이터를 보존합니다.
- 동기 함수와 비동기 함수를 구분합니다.
- 예외를 잡았다면 의미 없이 삼키지 않습니다.
인자를 받는 데코레이터는 함수가 한 단계 더 필요하다
@retry(max_attempts=3)처럼 설정을 전달하려면 바깥 함수가 설정을 받고, 그 안의 함수가 대상 함수를 받으며, 마지막 wrapper가 실제 호출을 처리합니다. 단계가 늘어날수록 이름을 명확히 짓고 각 클로저가 무엇을 보관하는지 확인해야 합니다. 설정값은 데코레이터가 적용되는 import 시점에 평가될 수 있으므로 사용자별로 달라지는 정보나 현재 시간을 잘못 캡처하지 않게 주의합니다.
재시도 데코레이터의 안전 조건
모든 예외를 자동 재시도하면 인증 실패나 잘못된 입력처럼 반복해도 해결되지 않는 오류가 지연만 늘립니다. 네트워크 일시 오류처럼 회복 가능한 예외만 지정하고 재시도 사이에 지수 백오프와 무작위 지연을 적용합니다. 결제와 데이터 생성처럼 부수 효과가 있는 함수는 멱등성이 보장되지 않으면 재시도로 중복 처리가 생길 수 있습니다. 최대 횟수와 총 제한 시간을 두고 마지막 예외를 호출자에게 전달해야 합니다.
실무 원칙: 재시도는 오류를 숨기는 장치가 아니라 일시 실패만 제한적으로 복구하는 정책이며, 대상 작업의 멱등성이 먼저 확인돼야 합니다.
로깅 데코레이터에서 개인정보 보호하기
함수 인자 전체를 로그에 남기면 비밀번호, 토큰, 개인 식별 정보가 노출될 수 있습니다. 함수명과 요청 ID, 처리 시간, 성공 여부를 기본으로 기록하고 값이 꼭 필요한 필드는 허용 목록과 마스킹을 적용합니다. 오류를 기록할 때는 스택 추적을 남기되 같은 예외가 여러 계층에서 중복 기록되지 않게 책임 위치를 정합니다. 운영 로그의 목적은 모든 값을 저장하는 것이 아니라 문제 흐름을 재구성하는 것입니다.
비동기 함수에는 비동기 wrapper 사용하기
async 함수를 일반 wrapper로 감싸면 반환값은 실행 결과가 아니라 코루틴 객체입니다. 비동기 데코레이터의 wrapper도 async로 선언하고 내부에서 await func(...)를 사용해야 실행 시간과 예외를 올바르게 측정할 수 있습니다. 하나의 데코레이터가 동기와 비동기를 모두 지원해야 한다면 inspect로 대상 함수를 구분해 각각 다른 wrapper를 반환하는 방식을 고려합니다.
데코레이터 순서가 결과를 바꾼다
여러 데코레이터를 쌓으면 함수에 가까운 데코레이터부터 적용되고 호출할 때는 바깥쪽부터 실행됩니다. 권한 검사와 캐시 순서를 잘못 두면 권한 확인 전에 공유 캐시가 반환될 수 있습니다. 트랜잭션과 재시도 순서도 각 시도가 독립 트랜잭션인지 전체 재시도가 한 트랜잭션인지에 영향을 줍니다. 적용 순서를 코드 리뷰 항목으로 만들고 작은 테스트로 실행 흐름을 확인합니다.
클래스 데코레이터와 descriptor 주의점
호출 횟수처럼 상태를 보관하려고 클래스 인스턴스를 데코레이터로 사용할 수 있습니다. 하지만 메서드를 감쌀 때 descriptor 바인딩을 제대로 처리하지 않으면 self 전달 문제가 생길 수 있습니다. 대부분의 경우 함수 클로저가 단순하며, 공유 상태가 꼭 필요하다면 동시 실행 안전성과 인스턴스 간 격리를 함께 검토해야 합니다.
테스트 순서
- 인자와 반환값이 원본과 동일하게 전달되는지 확인합니다.
- 원래 함수명과 설명문이 보존되는지 검사합니다.
- 성공과 지정 예외, 예상 밖 예외를 각각 시험합니다.
- 비동기 함수의 await와 취소가 정상 동작하는지 확인합니다.
- 여러 데코레이터의 실행 순서를 기록해 검증합니다.
- 민감 정보가 로그에 포함되지 않는지 점검합니다.
- 재시도 대상 함수의 중복 실행 안전성을 테스트합니다.
데코레이터가 업무 규칙까지 감추기 시작하면 일반 함수나 명시적인 서비스 호출이 더 읽기 쉬울 수 있습니다. 인증, 기록, 시간 측정처럼 호출 전후에 일관되게 적용되는 작은 동작에 사용하고, 데이터 변경 흐름은 코드에서 드러나게 유지하는 것이 좋습니다. 단순한 문법 뒤의 실행 시점과 래핑 구조를 이해하면 데코레이터를 유지보수 가능한 실무 도구로 사용할 수 있습니다.
한 줄 요약: 파이썬 데코레이터는 wraps와 예외·비동기·적용 순서를 지키고 숨겨진 부수 효과를 최소화할 때 안전합니다.