데코레이터에 대해 알기전에 아래 두 포스팅을 보고 오면 이해가 빠릅니다.
데코레이터는 크게 어려운 개념이 아니다. 사전적인 의미로는 "장식" 등의 의미를 가진다. 이름 그대로 Python 의 데코레이터는 코드를 '장식'해준다고 생각하면 편하다.
정말 쉽게 설명하면 다음과 같다
"""
<조금씩 다른 앞부분>
메인 로직
<조금씩 다른 뒷부분>
"""
메인 로직은 동일하지만 함수마다 로직의 앞부분과 뒷부분에 변화를 주고싶다면 사용할 수 있는 방법이 데코레이터이다.
바로 예제 코드를 통해서 데코레이터를 알아보자
- Python를 사용하는 웹 프레임워크중 하나인 Django에서 사용하는 데코레이터 느낌으로 작성해보았다.
데코레이터 기본 사용법
import datetime
def masterAuthenticated(func):
def wrapper(*args, **kwargs):
print("Check Permission...")
if kwargs["roll"] != "master":
return "403 Forbidden"
func(*args, **kwargs)
print(datetime.datetime.now())
return wrapper
@masterAuthenticated
def sendMessage(roll, msg):
print(roll, ":", msg)
@masterAuthenticated
def createPost(roll, post):
print("Post Created by", roll)
print(post)
print()
sendMessage(roll="master", msg="hello")
print()
print(sendMessage(roll="AnonymousUser", msg="hello"))
print()
print()
createPost(roll="master", post={"title":"공지", "content":"공지입니다."})
print()
print(createPost(roll="AnonymousUser", post={"title":"공지", "content":"나도 공지 올릴래"}))
print()
result:
Check Permission...
master : hello
2021-12-03 20:35:20.817552
Check Permission...
403 Forbidden
Check Permission...
Post Created by master
{'title': '공지', 'content': '공지입니다.'}
2021-12-03 20:35:20.818084
Check Permission...
403 Forbidden
masterAuthenticated(func)
함수를 실행한 유저가 master로 인증되었는지 확인하는 데코레이터이다.
백엔드에서는 기본적으로 로그인된 유저의 정보를 확인하고 유저에 따라서 권한을 나누는 작업을 수행한다. 이는 API마다 특정 유저의 접근을 제한하는 경우에 필수적이다. 예를 들어 특정 포스트에 유해한 문구가 있어 삭제조치를 해야하는 경우에 관리자 또는 글 작성자는 포스트를 삭제할 수 있다. 하지만 이외의 다른 유저들은 해당 포스트의 수정, 삭제 API에 접근할 수 없어야 한다. 따라서 다양한 API에 공통적으로 사용될 수 있고, 반복 작업이 요구되는 경우에 데코레이터를 사용하면 매우 효율적이다.
데코레이터가 감싸고 있는 함수의 매개변수들은 wrapper 함수의 매개변수에 똑같이 적어주어야 한다.
wrapper 함수에서 데코레이터가 감싸는 함수(func)를 실행하기 때문이다.
wrapper 함수 내부에서는 현재 roll이 master가 아니면 403 Forbidden을 리턴한다.
만약 현재 roll이 master라면 본 함수를 실행하고, 현재 시간을 출력하고 종료한다.
매개변수를 갖는 데코레이터
import datetime
def masterAuthenticated(method:str):
def decorator(func):
def wrapper(*args, **kwargs):
print("Check method...")
if method.lower() != "post":
raise RuntimeError("POST method only")
else:
print("OK")
print("Check Permission...")
if kwargs["roll"] != "master":
raise PermissionError("403 Forbidden")
else:
print("OK")
func(*args, **kwargs)
print(datetime.datetime.now())
return wrapper
return decorator
@masterAuthenticated(method="POST")
def sendMessage(roll, msg):
print(roll, ":", msg)
@masterAuthenticated(method="GET")
def createPost(roll, post):
print("Post Created by", roll)
print(post)
print()
sendMessage(roll="master", msg="hello")
print()
createPost(roll="master", post={"title":"공지", "content":"공지입니다."})
print()
result:
Check method...
OK
Check Permission...
OK
master : hello
2021-12-03 20:56:16.310796
Check method...
RuntimeError: POST method only
매개변수를 받는 데코레이터를 사용하기 위해서는 함수를 하나 더 감싸주어야 한다.
데코레이터가 method라는 이름의 매개변수를 받기 시작했다.
wrapper 함수에서 method에 따라서 작업을 할지말지 결정해준다.
클래스 데코레이터
import datetime
class masterAuthenticated:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Check Permission...")
if kwargs["roll"] != "master":
raise PermissionError("403 Forbidden")
else:
print("OK")
self.func(*args, **kwargs)
print(datetime.datetime.now())
@masterAuthenticated
def sendMessage(roll, msg):
print(roll, ":", msg)
@masterAuthenticated
def createPost(roll, post):
print("Post Created by", roll)
print(post)
print()
sendMessage(roll="master", msg="hello")
print()
createPost(roll="master", post={"title":"공지", "content":"공지입니다."})
print()
result:
Check Permission...
OK
master : hello
2021-12-03 21:12:28.183480
Check Permission...
OK
Post Created by master
{'title': '공지', 'content': '공지입니다.'}
2021-12-03 21:12:28.183536
1. __init__ 함수를 통해서 본 함수를 가져온다.
2. __call__ 함수가 기존의 wrapper 함수의 역할을 대신한다.
매개변수를 갖는 클래스 데코레이터
import datetime
class masterAuthenticated:
def __init__(self, method:str):
self.method = method
def __call__(self, func):
def wrapper(*args, **kwargs):
print("Check method...")
if self.method.lower() != "post":
raise RuntimeError("POST method only")
else:
print("OK")
print("Check Permission...")
if kwargs["roll"] != "master":
raise PermissionError("403 Forbidden")
else:
print("OK")
func(*args, **kwargs)
print(datetime.datetime.now())
return wrapper
@masterAuthenticated(method="POST")
def sendMessage(roll, msg):
print(roll, ":", msg)
@masterAuthenticated(method="GET")
def createPost(roll, post):
print("Post Created by", roll)
print(post)
print()
sendMessage(roll="master", msg="hello")
print()
createPost(roll="master", post={"title":"공지", "content":"공지입니다."})
print()
result:
Check method...
OK
Check Permission...
OK
master : hello
2021-12-03 21:19:56.394481
Check method...
RuntimeError: POST method only
1. __init__ 함수가 가장 먼저 실행되고, 매개변수를 받는다.
2. __call__ 함수가 본 함수(func)를 매개변수로 가지고 실행된다.
3. __call__내부의 wrapper 함수가 실행되고, 본 함수(func)를 실행한다.
이때 wrapper 함수에는 self 인자를 적으면 안된다는걸 주의하자
'Lang > Python' 카테고리의 다른 글
[Python] Closure (클로저) (0) | 2021.12.02 |
---|---|
[Python] First-Class Function (일급 함수) (2) | 2021.12.02 |
Tkinter 로 계산기 만들기 (0) | 2020.06.18 |