소개
Python open source 나 framework 코드를 보면 함수 앞에 @를 통해 사용되는 Decorators 를 많이 볼 수 있습니다. 이 데코레이터를 이용하면 보다 효율적인 코딩을 할 수 있는데요. 이것이 무엇인지, 언제 사용하는 것인지, 어떻게 사용하는 것인지를 정리 해볼까 합니다.
Decorators 가 무엇인가?
Python에서 함수(Function)는 일급함수(First-class Functions)입니다.
일급함수가 무엇인가요?? 일급함수는,
- 다른 함수에 매개변수로 전달할 수 있고,
- return 으로 반환 할수도 있고,
- 변수에 할당 할수도 있는 함수를 말합니다.
그리고 파이썬에서 함수는 중첩 함수로도 만들수가 있습니다. 함수 안에 함수를 만들 수 있다는 뜻이지요.
이런 함수의 특징을 이용해서 데코레이터 함수(또는 클래스로)를 만들 수 있습니다.
데코레이터는 A라는 특정 함수를 매개변수로 받아, A함수가 실행되기 전이나 후에 특정 로직을 먼저 실행하게끔 “wrap” 할 수 있는 함수를 말합니다. 예를 들어 아래와 같은 코드가 있다고 한다면,
@logger
def output():
print('execute output function')
output() # output 함수 실행
이 코드를 말로 풀어서 설명을 해보자면, “사전에 정의된 로깅하는 기능의 logger 데코레이터 함수로 output 함수를 감싸겠다” 라는 의미입니다. 즉, 이는 output 함수를 실행 하면, 그 전이나 후에 로깅기능을 수행하게 하겠다라는 의미를 가집니다.
어떻게 Decorators 를 사용하면 되는가?
그렇다면 데코레이터는 어떻게 구현이 되는 걸까요?데코레이터를 사용하기 위해서는 wrap 하기 위한 함수를 사전에 구현하여야 합니다.
아래는 @logger 데코레이터를 구현하고 사용해본 예제 코드입니다.
# @logger 데코레이터 구현 함수
def logger(func):
def wrapper_func():
print('execute wrapper function')
return func()
return wrapper_func
@logger
def output():
print('execute output function')
output()
""" 출력
execute wrapper function
execute output function
"""
- output() 호출
- @logger 데코레이터 로직 실행
- @logger 데코레이터에 의해 logger 함수의 인자로 output 함수 전달
- 내부함수 wrapper_fucn 리턴
- 리턴된 wrapper_func() 실행
- 인자로 받았던 logger 함수실행 결과를 리턴
말로 설명하자니 복잡해 보일수 있지만, 아래의 코드를 보면 좀더 쉽게 이해가 갈 것 같아요.
데코레이터가 실제로 동작하기 위해 자동으로 실행하게 되는 호출 로직이에요.
def logger(func):
def wrapper_func():
print('execute wrapper function')
return func()
return wrapper_func
def output():
print('execute output function')
# @logger 데코레이터에 의해 실제 동작되는 코드들 (@logger 를 앞에 붙인 것과 동일)
output_logger = logger(output)
output_logger()
""" 출력
execute wrapper function
execute output function
"""
Decorators 를 사용하면 무엇이 좋은가?
위의 예제를 참고 삼아 이야기를 해보도록 하겠습니다.
최초에 로깅을 생각하지 않고, output 함수를 구현하였다고 가정해봅니다. 그런데 프로그램 개발을 지속하다가 output 함수에 로깅이 필요하게 되었다고 쳐봅시다. 보통은 어떻게 하게 될까요?
output 함수 내부에 로깅을 위한 로깅기능의 로직을 추가하게 될 것입니다. 그런데 output 함수 이외에도 프로그램 내 모든 함수들에 로깅 기능을 추가하여야 한다면 어떨까요? 일일이 함수 내부에 로깅 기능을 구현 해야 할 것입니다. 엄청난 작업이 되겠죠..
그런데 데코레이터를 사용하게 된다면 로깅 기능을 하는 로직의 데코레이터 함수만 만들어 놓고, @logging 형식으로 함수 정의 전에 데코레이터만 추가해준다면 작업이 훨씬 간편하게 마무리가 되게 됩니다.
한마디로 함수별 공통 기능을 데코레이터로 구현하여, 보다 효율적으로 사용할 수 있게 하기 위함입니다.
언제 Decorators 를 사용하는 것이 좋은가?
앞서 데코레이터를 사용함으로 얻을 수 있는 장점을 보았는데요, 그렇다면 언제 데코레이터를 사용하면 좋을까요?
자주 사용되는, 사용되면 좋을 유즈 케이스는 아래와 같습니다.
- logging
- 접근제어와 인증 강제
- 타이머 기능(함수 실행 소요 시간 계산 같은)
- rate limiting
- caching
모두 호출할려는 함수의 시작 또는 끝에 특정 로직 추가를 통해 구현할 수 있는 케이스들입니다.
이외에도 데코레이터의 동작방식을 이해하고 있다면, 더욱 많은 유즈 케이스가 있을 수 있겠죠.
공개되어있는 오픈소스등을 찾아보며, 데코레이터가 어떻게 사용되고 있는지, 어떻게 구현되어 있는지 한번 살펴 본다면 효율적인 파이썬 코딩에 많은 도움이 되지 않을까 합니다.