이번에는 파이썬 데코레이터에 대해 정리해봅시다.
데코레이터는 장식하다, 꾸미다라는 뜻의 decorate에 er(or)를 붙인 말로 장식해주는 것, 꾸며주는 것 이라고 생각하면 된다.
Django나 DRF 코드를 작성하다 보면 '@'가 붙어있는 코드를 볼 수가 있는데, 이 @가 바로 데코레이터를 의미한다.
말이 나왔으니 Django 데코레이터 예제를 한번 살펴보자.
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def my_view(request):
# ...
pass
이 코드는 공식문서에서 볼 수 있다.
중간에 @require_http_methods(["GET", "POST"])
가 바로 데코레이터라고 하는 것이고, 이 코드에서는 GET, POST 요청 메서드만 수락해주는 기능이다.
그럼 본격적으로 왜 써야 하는지, 어떻게 구현되어 있는지, 어떻게 동작되는지 확인해 보자.
함수의 시작과 끝을 print로 알려주는 함수를 작성해보겠다.
def hello():
print('hello 함수입니다.')
print('hello')
print('hello 함수가 끝났습니다.')
def world():
print('world 함수입니다.')
print('world')
print('world 함수가 끝났습니다.')
hello()
world()
이렇게 작성할 수 있을 것이다.
하지만 보다시피 코드 중복이 발생하고 코드 변경이 번거로워진다.
코드를 좀 줄여보자.
def deco(func):
def wrapper():
print(func.__name__, '함수 입니다.')
func()
print(func.__name__, '함수가 끝났습니다.')
return wrapper
def hello():
print('hello')
def world():
print('world')
hello_func = deco(hello)
hello_func()
world_func = deco(world)
world_func()
wrapper라는 클로저를 이용하여 함수의 시작과 끝을 반환할 수 있도록 했다. 함수 안에서 함수를 만들고 반환한다.
(클로저란? 클로저는 자신을 둘러싼 함수 스코프의 상태값을 참조하는데, 이 값은 함수가 메모리에서 사라져도 값이 유지 된다. 더 자세한 내용은 다음 포스팅에서 다뤄보겠다.)
위 코드를 정말 데코레이터 답게 만들어보자면
# 더 간단히 표현할 수 있음
def deco(func):
def wrapper():
print(func.__name__, '함수 입니다.')
func()
print(func.__name__, '함수가 끝났습니다.')
return wrapper
@deco
def hello():
print('hello')
@deco
def world():
print('world')
# trace_hello = deco(hello)
# trace_hello()
hello()
# trace_world = deco(world)
# trace_world()
world()
데코레이터 문법을 사용하여 작성해 보았다.
좀 더 가독성있고 중복이 사라진 것을 확인할 수 있다.