Decorator 클래스는 __init__ 메서드를 통해 적용할 함수/메서드를 저장하고, __call__ 메서드로 호출될 때의 동작(대체로 적용할 함수가 실행되기 전에 실행할 코드)를 정의한다.
class Decorator:
def __init__(self, function):
self.function = function
# self 다음 들어가는 인자들은 Decorator 클래스가 적용될 함수에 입력될 인자들이 들어간다.
def __call__(self, *args, **kwargs):
# Decorator가 적용될 함수를 호출하기 전에 실행될 코드들
print("Hello World!")
return self.function(*args, **kwargs)
@Decorator
def func1(*args, **kwargs):
print(args, kwargs)
func2(1,2,3,4,text="Hi!")
# Hello World!
# (1, 2, 3, 4) {'text': 'Hi!'}
참고로 decorator를 함수로 이용하는 방법은 아래와 같다..
def decorator(function):
def wrapper(request):
print("Hello World!")
return function(request)
return wrapper
@decorator
def func2(request):
print(request)
func2("Hi!")
# Hello World!
# Hi!
이런 구조를 활용한다면, django 뷰에 선언된 함수가 실행되기 전에 요청에 붙은 토큰이 유효한지 확인(ex.로그인이 되어 있는지)하거나, 권한이 있는 유저인지 판별하는 로직을 일일이 추가할 필요 없이, import해서 @decorator 형태로 붙일 수 있을 것이다.
# utils.py
class LoginDecorator:
def __init__(self, function):
self.function = function
def __call__(self, request, *args, **kwargs):
token = request.headers.get("Authorization", None)
try:
if token:
token_payload = jwt.decode(token, SECRET_KEY, algorithms='HS256')
# 유저 모델에서 인스턴스를 찾아 request의 속성으로 부여
request.user = User.objects.get(id=token_payload['user_id'])
return self.function(self, request, *args, **kwargs)
return JsonResponse({"message":"NEED_LOGIN"}, status=401)
except DecodeError:
return JsonResponse({"message":"INVALID_USER"}, status=401)
except User.DoesNotExist:
return JsonResponse({"message":"INVALID_USER"}, status=401)
위 decorator를 적용하고, 로그인한(토큰을 부여받은) 사용자가 자신의 이름을 조회할 수 있는 간단한 뷰를 작성하여 decorator 작동 여부를 확인했다.
# views.py
from utils import LoginDecorator
class LoginView(View):
@LoginDecorator
def get(self,request):
return JsonResponse({"YOUR_NAME":request.user.name}, status=200)
위 캡쳐와 같이 헤더에 Authorization: '부여받은 토큰' 을 붙여 GET 메소드로 HTTP 요청을 보낸 결과, 의도대로 작동하는 것을 확인하였다.
만약 토큰이 없을 경우 NEED_LOGIN, 틀린 형식의 토큰일 경우 INVALID_USER가 나오게끔 하였다(401 Unauthorized).