본문 바로가기
카테고리 없음

데코레이터와 클로저 이해하기

by BOOST YOUR INFORMATION 2026. 7. 19.

데코레이터와 클로저 이해하기
데코레이터와 클로저

1. 기본/개요

파이썬에서 함수는 정수나 문자열처럼 다른 변수에 할당하거나, 인자로 전달하거나, 반환값으로 돌려줄 수 있는 "일급 객체(first-class object)"입니다. 클로저(closure)는 이 성질을 바탕으로 등장하는 개념으로, 내부 함수가 자신을 감싸고 있는 바깥 함수(enclosing function)의 지역 변수를 그 바깥 함수가 실행을 마친 뒤에도 계속 "기억"하는 함수를 말합니다. 예를 들어 outer(msg)라는 함수 안에 inner()라는 함수를 정의하고 innermsg를 참조한 채 반환되면, outer의 실행이 끝난 뒤에도 innermsg 값을 기억합니다.

데코레이터(decorator)는 이 클로저 구조를 활용해 만들어지는 문법으로, 다른 함수(또는 클래스)를 인자로 받아 수정된 버전을 반환하는 함수입니다. @ 기호로 시작하는 데코레이터 문법은 실제로는 add = my_decorator(add)와 같은 코드의 문법적 설탕(syntactic sugar)에 불과합니다. 파이썬 표준 라이브러리의 @property, @staticmethod, @classmethod, @functools.wraps, @functools.lru_cache가 대표적인 내장 데코레이터이며, Flask의 @app.route, pytest의 @fixture, dataclasses@dataclass 등 주요 프레임워크의 핵심 동작 방식이기도 합니다.

제 개인적인 생각으로는, 데코레이터와 클로저는 파이썬 입문자가 "함수형 프로그래밍적 사고"로 넘어가는 첫 관문이라고 봅니다. 처음 접하면 "함수가 함수를 반환한다"는 개념 자체가 낯설어 혼란스러울 수 있지만, 일단 감을 잡으면 반복되는 로깅·타이머·인증 체크 같은 코드를 놀랍도록 깔끔하게 재사용할 수 있게 됩니다. 다만 남용하면 디버깅이 어려워지고 스택 트레이스가 지저분해지는 부작용도 분명히 존재합니다.

2. 기능/스펙

  • 클로저의 핵심 조건: (1) 중첩 함수여야 하고, (2) 내부 함수가 외부 함수의 변수를 참조해야 하며, (3) 외부 함수가 그 내부 함수를 반환해야 합니다.
  • 데코레이터 기본 패턴: wrapper(*args, **kwargs) 형태로 원본 함수의 모든 인자를 그대로 전달받아 감싸는 것이 표준적인 작성법입니다.
  • functools.wraps: 데코레이터로 함수를 감싸면 원본 함수의 __name__, __doc__ 같은 메타데이터가 wrapper 함수의 것으로 덮어씌워지는 문제가 발생합니다. @wraps(func)를 사용하면 이 메타데이터를 보존할 수 있습니다.
  • 인자를 받는 데코레이터(데코레이터 팩토리): @retry(times=3)처럼 데코레이터 자체에 인자를 넘기려면 함수를 3중으로 중첩해야 합니다.
  • 클래스 데코레이터: 함수뿐 아니라 클래스 전체를 인자로 받아 속성을 추가하거나 동작을 바꾸는 것도 가능합니다.
  • 최신 동향: ParamSpec(파이썬 3.10, PEP 612)은 함수의 전체 매개변수 시그니처를 그대로 캡처할 수 있게 해, 데코레이터로 감싼 함수도 IDE 자동완성과 타입 검사를 유지하도록 돕습니다. 또한 파이썬 3.13에서는 PEP 702를 통해 새로운 warnings.deprecated() 데코레이터가 도입되어, 정적 타입 검사기에 함수나 클래스의 사용 중단(deprecation) 사실을 알리고 런타임에서도 DeprecationWarning을 발생시킬 수 있게 되었습니다.

3. 장단점

장점

  • 기존 함수의 소스 코드를 건드리지 않고 기능(로깅, 캐싱, 인증, 재시도 등)을 추가할 수 있어 관심사 분리(separation of concerns)에 유리합니다.
  • 클로저를 활용하면 전역 변수 없이도 상태를 캡슐화할 수 있어 간단한 카운터나 설정값 저장에 유용합니다.
  • 코드 재사용성이 높아지고, 프레임워크 수준에서 선언적(declarative) 스타일의 API를 제공할 수 있습니다.

단점(제 비판적 견해 포함)

  • 데코레이터가 여러 겹으로 중첩되면 실제 실행 흐름을 추적하기 어려워지고, 예외 발생 시 스택 트레이스가 wrapper 함수들로 뒤덮여 원인 파악이 힘들어집니다.
  • @wraps를 빠뜨리는 실수가 매우 흔하며, 이 경우 help()나 문서 자동화 도구가 엉뚱한 정보를 보여주는 등 디버깅 난이도를 은근히 높입니다.
  • 클로저의 변수 캡처는 "값"이 아니라 "참조"를 캡처하기 때문에, 반복문 안에서 클로저를 여러 개 만들 때 의도치 않게 마지막 반복 값만 캡처되는 고전적인 버그(late binding)가 자주 발생합니다. 이는 입문자뿐 아니라 숙련자도 종종 빠지는 함정입니다.
  • 과도한 데코레이터 사용은 "마법처럼 보이는 코드"를 만들어, 팀 내 코드 리뷰나 온보딩 시 진입 장벽을 높일 수 있습니다.

4. 추천대상

  • 함수형 프로그래밍 개념을 파이썬에서 실전 활용하고 싶은 중급 개발자
  • Flask, FastAPI, pytest 등 데코레이터 기반 API를 사용하는 프레임워크의 내부 동작 원리를 이해하고 싶은 사람
  • 반복되는 로깅·캐싱·타이머 로직을 직접 만들어 코드 중복을 줄이고 싶은 실무 개발자
  • 반대로, 이제 막 파이썬 함수와 클래스의 기본 문법을 익힌 초급자에게는 다소 이른 주제일 수 있으므로 일급 객체 개념부터 충분히 익힌 뒤 접근하는 것을 추천합니다.

5. FAQ

Q1. 클로저와 데코레이터는 같은 개념인가요?
아닙니다. 클로저는 "함수가 외부 변수를 기억하는 구조" 자체를 말하고, 데코레이터는 그 클로저 구조를 활용해 다른 함수를 감싸는 "적용 패턴"입니다. 데코레이터는 대부분 클로저를 이용해 구현되지만, 클로저가 반드시 데코레이터 형태로만 쓰이는 것은 아닙니다.

Q2. @wraps를 안 쓰면 실제로 어떤 문제가 생기나요?
데코레이터로 감싸진 함수의 __name__, __doc__, __module__ 등이 원본이 아닌 wrapper 함수의 값으로 바뀝니다. 이는 디버깅 로그, 자동 문서화 도구, help() 명령 등에서 잘못된 정보를 보여주는 원인이 됩니다.

Q3. 데코레이터에 인자를 여러 개 넘기려면 어떻게 하나요?
함수를 3단계로 중첩합니다: 가장 바깥 함수가 데코레이터의 인자를 받고, 중간 함수가 실제 함수를 인자로 받으며, 가장 안쪽 함수(wrapper)가 실제 실행을 담당합니다.

Q4. 비동기(async) 함수에도 데코레이터를 쓸 수 있나요?
가능합니다. 다만 wrapper 함수도 async def로 정의하고 await func(*args, **kwargs) 형태로 호출해야 하며, 동기 함수용 데코레이터를 그대로 비동기 함수에 적용하면 코루틴 객체가 실행되지 않고 그대로 반환되는 문제가 생길 수 있습니다.

6. 출처

  • Python 3.13 공식 문서, "What's New In Python 3.13" (PEP 702 관련 항목), docs.python.org
  • Python GitHub 저장소 cpython, Doc/whatsnew/3.13.rst
  • devtoolbox.dedyn.io, "Python Decorators: The Complete Guide for 2026" (ParamSpec, PEP 612 관련 서술)
  • 이 글에서 다룬 개념 설명(클로저 정의, 데코레이터 문법 자체)은 파이썬 언어의 안정된 표준 문법으로, 별도의 최신 통계나 수치 주장은 포함하지 않았습니다. 확인이 어려운 세부 벤치마크나 수치는 이 글에 포함하지 않았습니다.

소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 ⚡ 정보 부스터 🚀