Instagram clone - 로그인 데코레이터 + CommentView

Joey Lee·2020년 5월 20일
1

Django

목록 보기
9/23

사용자가 로그인해야만 쓸 수 있는 API의 경우, 사용자가 로그인을 했는지 안 했는지 사전에 체크해야 한다. 이 역할을 로그인 데코레이터가 한다. 로그인을 한 경우에는 해당 API를 실행시키고, 로그인을 하지 않았을 경우 로그인 유도를 해야 하다.

아래 케이스는 유효한 access_token이 없을 경우 로그인 페이지로 리다이렉트 하는 대신 JSON으로 {"message" : "INVALID_TOKEN"}를 리턴해 주는 함수이다.

1. 로그인 데코레이터 함수 만들기

login_decorator가 장식한 함수를 func 파라미터로 데코레이터 함수에 전달하고, 최종적으로 wrapper 함수를 리턴

1) wrapper 함수의 구성 로직

  • request.headers에 담겨 있는 'Authorization'의 value, 즉 access_tokenauth_token(str타입)에 담음

  • jwt.decode에 첫번째 인자로 디코딩할 대상이 되는 auth_token을, 두번째 인자는 'secret', 세번째로 algorithm 타입을 넣어서 dictionary 형태의 사용자 정보를 payload 변수에 담음

    ex) eyJ0eXAiO...iJK -> {'user_id' : 1}

  • payload의 user_id 값과 동일한 id를 가지는 User 객체를 request.user 변수에 담음

  • func에 request를 인자로 전달하고 실행하도록 함

예외 처리

  • User.DoesNotExist : 해당 user_id를 가지고 있는 User 객체가 없을 경우, {"message" : "INVALID_USER"} 리턴

  • jwt.exceptions.DecodeError : access_token이 없거나 일치하지 않을 경우, {"message" : "INVALID_TOKEN"} 리턴

2. Login Decorator 함수 전체 코드

import json, jwt
from django.http import JsonResponse
from .models import User

def login_decorator(func):
  def wrapper(self, request, *args, **kwargs):

    try:
      auth_token = request.headers.get('Authorization', None)
      # print('auth_token', auth_token)
      payload = jwt.decode(auth_token, 'secret', algorithm='HS256')
      # print('payload', payload)
      request.user = User.objects.get(id=payload['user_id'])
      return func(self, request, *args, **kwargs)

    except User.DoesNotExist:
      return JsonResponse({"message" : "INVALID_USER"}, status=400)
    except jwt.exceptions.DecodeError:
      return JsonResponse({"message" : "INVALID_TOKEN"}, status=400)
    
  return wrapper

2. 데코레이터 함수 적용하기

CommentView클래스는 코멘트를 작성하는 post함수와 작성한 코멘트를 보여주는 get함수로 구성되어져 있습니다. 이 함수들은 사용자가 로그인 되어 있을 때만 작동하는 함수들입니다.

따라서 이 2개 함수 앞에 로그인 데코레이터를 추가하여, 로그인이 되었을 경우에만 2개 함수가 실행되도록 세팅을 합니다.

1) 데코레이터 추가하는 코드

@login_decorator

2) 데코레이터 적용한 CommentView 전체 코드

import json
from django.views import View
from django.http import JsonResponse
from user.views import login_decorator
from .models import Comment

class CommentView(View):
  @login_decorator
  def post(self, request):
    data = json.loads(request.body)
    user = User.objects.get(id=request.user.id)
    # request.user로 전달된 id값과 일치하는 객체 생성 
    
    Comment(
        author = user, # 객체를 담음 (엄밀하게는 user_id가 담김)
        content = data['content']
    ).save()

    return JsonResponse({'message' : 'SUCCESS'}, status=200)
    
  @login_decorator
  def get(self, request):
    comment_data = Comment.objects.values()
    return JsonResponse({'comments' : list(comment_data)}, status=200)

3. 실행 결과 : 코멘트 작성 (httpie)

❯ http -v http://127.0.0.1:8000/feed/comment/ 'Authorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMX0.8WeBhffU-wMdOhE4zdS3KJx2XfCYVpAoPNwSjaRovn4' content='어제는 흐리더니 오늘은 날씨가 참좋네'
POST /feed/comment/ HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMX0.8WeBhffU-wMdOhE4zdS3KJx2XfCYVpAoPNwSjaRovn4
Connection: keep-alive
Content-Length: 115
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.1.0

{
    "content": "어제는 흐리더니 오늘은 날씨가 참좋네"
}

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Date: Wed, 20 May 2020 07:56:20 GMT
Server: WSGIServer/0.2 CPython/3.7.7
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "SUCCESS"
}

4. 실행 결과 : 코멘트 보기 (httpie)

http -v http://127.0.0.1:8000/feed/comment/ 'Authorization:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMX0.8WeBhffU-wMdOhE4zdS3KJx2XfCYVpAoPNwSjaRovn4'
GET /feed/comment/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMX0.8WeBhffU-wMdOhE4zdS3KJx2XfCYVpAoPNwSjaRovn4
Connection: keep-alive
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.1.0

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Date: Wed, 20 May 2020 08:20:46 GMT
Server: WSGIServer/0.2 CPython/3.7.7
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "comments": [
        {
            "author_id": 1,
            "content": "‘코멘트를’",
            "created_at": "2020-05-11T11:24:51.059Z",
            "id": 1,
            "updated_at": "2020-05-12T07:40:45.690Z"
        },
        {
            "author_id": 1,
            "content": "코멘트를남겨요.",
            "created_at": "2020-05-11T11:25:09.152Z",
            "id": 2,
            "updated_at": "2020-05-12T07:40:34.498Z"
        },
        ... 이하 생략
profile
안녕하세요!

0개의 댓글