Django | Login Decorator

gemma. K·2020년 11월 14일

Decorator 함수란

  • decorator란 '꾸며준다'란 뜻
  • 함수 위에 추가되어 그 함수가 실행 되기 전 먼저 실행되는 함수
  • 형태적으로는 내부함수가 존재 하고, 매개 변수(parameter)로 본 함수를 받는다.
def decorator(original_func):
    def inner(*args, **kwargs):
        return original_func(*args, **kwargs)
    return inner
    
@decorator
def origin(x, y):
    return x + y
    
origin("Hello", "World") 
# Hello World

Decorator 활용

데코레이터는 본 함수가 실행 되기 전, 미리 생성된 함수를 실행하므로 효율적인 코드 재활용에 큰 도움이 된다. 상상해 보자 만약 어떤 사람이 회원가입을 하고 게시글을 보고 댓글을 쓰고 좋아요를 누를 때마다 사용자의 로그인 상태에 대해 서버는 알 수 없는 stateless 상태이기에 해당 로직에서 매번 사용자의 로그인의 상황을 체크하는 코드를 로직에 포함 시켜 놓아야 한다. 그러나 재사용에 유리한 Decorator를 이용한다면 매번 사용자의 상태를 확인하는 코드를 포함 시킬 필요가 없이 데코레이터를 통해 해당 사용자의 접근 권한에 대해 쉽게 판별할 수 있다.


Login Decorator

  • 프론트(클라이언트)는 헤더에 'Authrazation'라는 키 값으로 토큰을 담아 요청을 보낸다.
  • 해당 토큰의 복호화(decode) 하여 도출된 id가 유저 테이블에 존재하는 사용자인지 판별한다.
  • 만약 존재하는 사용자라면 매개변수로 받은 본 함수를 실행 시킨다.
  • 반대로 유저 테이블에 존재하지 않는 잘못된 토큰이라면 권한이 없다는 메시지를 담아 리턴해 준다.

첫 번째 방법

  • try, except로 에러를 잡아준다.
  • except로 에러를 잡는 것은 try 도중 나올 수 있는 error 지점에서 서버가 망가지지 않도록 하기 위함이다.
  • get 메소드로 'Authorization' 키에 담긴 token을 받으려 한다. 그렇지만 header에 'Authorization' 키가 없다면 None type 자료형이 담기도록 한다.
  • jwt.exceptions.DecodeError 또한 None 타입 자료를 decode 할 때, 발생하는 에러를 잡기 위함이다.
  • 토큰이 있었고, decode까지 완료된 경우, User 테이블에 해당 id를 가진 유저가 존재하는 지 확인 해야할 때 만약 없다면 User.DoesNotExist except로 잡아준다.
  • 토큰이 있고, 존재하는 유저 id라면 본 함수를 호출한다.
def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
    	try:
            auth_token = request.headers.get('Authorization', None)
            payload = jwt.decode(auth_token, SECRET_KEY, algorithm='HS256')
            request.user = User.objects.get(id=payload['id'])
            
            return func(self, request, *args, **kwargs)
            
        except jwt.exceptions.DecodeError:
            return JsonResponse({'message':'INVALID_TOKEN'}, status=401)
            
        except User.DoesNotExist:
            return JsonResponse({'message':'INVALID_USER'}, status=403)
   return wrapper

두 번째 방법

첫 번째 방법과 같은 방법이지만 try, except가 아닌 나올 수 있는 에러에 해당되는 if문으로 에러를 잡는다.

  • 먼저 헤더에 'Authorization'이란 키를 찾는다. 만약 없다면 INVALID_TOKEN를 리턴한다.
  • 만약 있다면 payload 변수에 복호화된 토큰을 담는다.
  • 그리고 User 테이블에 해당 id를 가진 유저가 존재하는 지 확인하고, 없다면 UNKNOWN_USER를 리턴한다.
  • request.user로 해당 db row를 담고, 본 함수를 호출한다.
def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
        if not 'Authorization' in request.headers:
            return JsonResponse({'message':'INVALID_TOKEN'}, status=401)
            
        payload = jwt.decode(request.headers['Authorization']), SECRET_KEY, alogorithm='HS256')
            
        if not User.objects.filter(id=payload['id']):
            return JsonResponse({'message':'UNKNOWN_USER'}, status=40
            
        request.user = User.objects.get(id=payload['id'])
            
        return func(self, request, *args, **kwargs)

Error Handling of BE.dev for Server 🤟🏻

이번 로그인 데코레이터 실습을 통해 얻은 것은 백엔드 개발자는 '서버가 절대 죽지 않도록 적절한 리턴 값을 프론트(클라이언트)에게 전달해 줘야 한다' 라는 말에 대한 답을 얻은 것 같다. 그 만큼 내가 쓴 코드에 어떤 에러가 나올 지 지금의 나는 예측하기 쉽지 않다. 그렇기에 더 더욱 코드를 치는 중간 중간에 에러를 확인하고 어떻게 에러를 잡을 지 고민해 보는 것이 중요한 것 같다.

0개의 댓글