2월 25, 2022

[django] westagram포스팅 구현, 로그인 데코레이터 구현

1. 특정 요청을 보낸 유저의 권한을 인가(확인)하는 데코레이터를 구현합니다 

2. westagram이라는 SNS에 게시물을 POST할 수 있는 기능을 구현하려 합니다. 


우선 데코레이터의 코드입니다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import jwt

from django.http import JsonResponse

from my_settings import SECRET_KEY, ALGORITHM
from users.models import User


def trace(func):
    def wrapper(self,request, *args, **kwargs):  
        try:
            access_token = request.headers['Authorization']     
            header = jwt.decode(access_token, SECRET_KEY,  algorithms=ALGORITHM)
            pk = header['id']

            User.objects.get(id=pk)
            request.pk = pk 

            return func(self, request, *args, **kwargs) 

        except User.DoesNotExist:
            return JsonResponse({"message":"Invalid User"}, status = 401)
        except jwt.DecodeError:
            return JsonResponse({"message":"Invalid token"}, status = 401) 
            
    return wrapper

  • trace는 실행되지 않은 wrapper라는 함수를 리턴합니다
  • wrapper가 실행됩니다
  • "access_token"이라는 변수에 요청의 헤더에 담긴 jwt토큰 값을 담습니다
  • 이를 jwt.decode()를 통하여 다시 decode 해주면 유저 id와 값이 담긴 딕셔너리가 나옵니다
  • "pk" 변수에 이 id 값을 담아줍니다
  • 인수로 받은 request라는 인스턴스에 .pk로 변수를 선언하여 pk 값을 저장합니다.
  • 예외처리는 두개를 해주었는데
    • 토큰을 발급받은 유저가 탈퇴 등으로 DB에서 삭제된 경우와(토큰은 여전히 유효)
    • 토큰이 잘못된 경우입니다.
  • 두번째 경우는 jwt.DecodeError를 통하여 예외처리를 해줍니다
  • 첫번째 경우는 User.objects.get(id=pk) 를 통해 나온 값이 없을 때, 토큰은 유효하지만 user가 이미 DB에서 삭제된 경우이기에 User.DoesNotExist를 사용해줍니다

에러가 발생하지 않은 경우, 데코레이터를 받는 함수를 그대로 리턴해줍니다.


다음으로 posting 기능을 구현해줍니다. Django에서는 다루는 데이터의 종류가 달라지는 시점에서 앱을 분리합니다. posting에 관한 데이터는 이전까지 다루지 않던 데이터이니 app을 분리해줘야 합니다. startapp으로 app을 만들어줍니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from djang.db import models
from users.models import User

class Posting(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    content = models.CharField(max_length=100, blank=True)
    class Meta:
        db_table = 'postings'

class Image(models.Model):
        posting = models.ForeignKey(Posting, on_delete=models.CASCADE)
        image = models.CharField(max_length=100)
        class Meta:
            db_table = 'images'

posting은 어떠한 유저가 올린 글이기에, 각 posting은 users app의 User클래스를 정참조합니다

posting 하나에 여러개의 사진이 등록될 수 있기에 images가 postings를 정참조 하도록 모델을 구현합니다. 

created_at은 auto_now_add 속성을 통하여 글을 올리는 시간을 기록해줍니다. 

그런데 이렇게 하니 updated_at도 같이 생성됩니다. 



PostingView는 posting과 담당된 기능을 총괄하는 클래스입니다.

우선, post함수를 구현하여 포스팅을 올릴 수 있게 해주고, 추후에 get으로 이를 받아 올 수 있도록 구현하겠습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import json

from django.http            import JsonResponse
from django.views           import View

from postings.models import Posting,Image
from postings.utils import trace
from users.models import User
# Create your views here.

class PostingView(View):
    @trace
    def post(self, request):
        data = json.loads(request.body)

        user_id = User.objects.get(id=request.pk) 
        content = data.get("content", '')
        image = data["image"]
 
        post = Posting.objects.create(
            user = user_id,
            content = content
        )

        for i in image:
            Image.objects.create(
                image = i,
                posting = post #Posting 객체를 넣어줬는데 알아서 테이블에 객체의 id값이 들어가는지? 
            )

 

        return JsonResponse({'message':'SUCCESS'}, status=201)

글을 올리려는 유저가 유효한 토큰을 가지고 있는지 확인하기 위하여 @trace로 데코레이터를 먼저 선언해줍니다.


앞서 데코레이터의 reqeust인스턴스에 pk라는 변수를 저장해주고, 이를 함수의 인자로 리턴해주었죠? 이를 이용하여 user FK컬럼에 글을 올리는 유저의 id값을 넣어줍니다.

이미지를 여러개 유저가 보내주었을시, 이를 각각 images 테이블에 넣기 위하여, 이를 for문을 통하여 각각 테이블에 넣어줘야 합니다.

앞서 만들어진 포스팅에 이미지가 들어가야 하므로, 방금 만들어진 포스팅 인스턴스를 담은 "post" 변수를 그대로 넣어줍니다. 

주석을 달아놓았듯이 Image 객체를 만들어줄때, posting값에 post라는 인스턴스를 그대로 할당하면 , sql상의 posting_id에는 해당 인스턴스의 id값이 알아서 잘 들어갑니다.