wecode TIL day 23 (Nov 10)

Jae Hoon Shin, 신재훈, Noah·2020년 11월 11일
0
post-thumbnail

Django Back-end 기초 프로젝트 인스타그램 클론!

이제까지 무엇을 어떻게 했는지 내가 한것을 내가 이해하고 있는지 되짚어보자

1. miniconda로 가상환경 세팅

협업하는 각각의 프로젝트가 사용하는 파이썬, 장고등의 언어, 프레임워크 버전이 다르기 때문에 서로 충돌이 나이 않기 위해 보통 프로젝트를 새로 생성할 때 해당 프로젝트를 위한 세팅!

장고 설치 Django install

가상환경에다가! 장고 설치

프로젝트 생성!

프로젝트 settings 설정

프로젝트 내 앱 생성!

settings에 내 앱 등록

모델 작성

Model 내부에는 향후 DB 엣 쌓아줄 데이터의 기준을 만든다.

데이타 테이블의 컬럼 을 만든다는 생각하면 편합니다.
Meta 클래스를 추가해서 db_table을 선언하면 이 값이 테이블 이름이 된다.

!!! 모델 작성 후 migration과 migrate를 해줘야 한다.

뷰 작성

작성된 모델을 바탕으로, 들어오는 데이터를 어떻게 처리할지에 대한 논리를 맡는다.
첫부분에는 우리가 사용할 module를 불어와야한다. 우리
HTTP를 통해 받은 request정보 json을 파이썬이 읽을수 있는 형태로 변환해주는 json을 import한다.
그리고 json형태로 응답해줄수있는 (response) JsonResponse모듈을 불러온다.

json.loads는 json 형태의 데이터를 파이썬이 읽을 수 있는 형태로 디코딩 해주는 역할

보통 get, post 요청이 들어오는데, get은 순전히 데이터만을 요청할 때, post는 어떠한 데이터를 입력, 수정을 할 때 라고 생각하면 됩니다. 예를 들어, 로그인도 로그인 완료라는 데이터를 요청해서 get일 것 같지만, 일단 데이터를 입력해서 데이터가 맞는지 확인 받기 때문에 post 메서드를 사용합니다.

요청(request)을 통해 들어온 json 데이터는 json.loads에 의해 파이썬이 읽을 수 있는 형태로 디코딩 됩니다.
이 데이터에서 우리가 필요한 데이터를 뽑아 우리가 만들었던 Account 모델의 DB에 저장합니다. 아직 형태는 익숙지 않은데, 모델명( 필드명 = 입력할 데이터).save()이 구조를 기억하세요!

저장이 완료되면, 요청을 한쪽으로 json 형태의 응답을 보내줍니다.
JsonResponse의 인자로는 회원가입 완료 메시지와 요청이 성공했음을 나타나는 status 200 코드를 리턴해줍니다.

경로 URL PATH 작성

urlpatterns = [
path('sign', include('account.urls')),
]
/sign << endpoint
account.urls 파일로 가라~

SignUpView.as_view() -> SignUpView(class) -> SignUpView(instance) -> GET or POST 확인 -> 메서드 매칭

as_view 는 GET 인지 POST 인지 기타등등인지 확인하고 그 역할을 해주게 하는 역할 입니다.

python manage.py runserver

http POST url

http -v http://127.0.0.1:8000/sign/up email=aaa@aaa.com password=1234

비밀번호 암호화,

비밀번호를 클라이언트로부터 받자마자 암호화를 해서 암호화된 형태로 DB에 넣을려고 한다.
일단 받은 비밀번호를 바이트 타입으로 인코딩해주고, 인코딩한 값을 HASH 값으로 암호화 해준다.
DB에 이 값을 넣을때는 다시 유니코드 문자열로 변환시켜준다.
그리고 DB에 저장하면 끝

로그인 view 비밀번호 확인 및 토큰 발행

여기서는 사용자가 입력한 비밀번호가 DB엣 있는 사용자의 이멜과 매칭되는 비밀번호와 일치하는지 확인해야한다.
만약 일치하면 토큰을 발행해서 HTTP응답에 같이 넘겨주면 된다.

비밀번호가 일치하는지 확인하는 것은,
"이전에 저정한 암호환된 비번" 과 "사용자가 이번에 입력한 비밀번호"를 bcrypt.check메서드로 비교해서 TURE,FALSE 결과값을 주는 것.

비밀번호 확인 후 토큰이 생성되면, JsonResponse로 토큰을 응답할 수 있도록 유니코드 문자열로 디코딩해서 값을 전달해준다.
토큰에 보통 아이디 정보를 넣어둔다. (이메일 정보 넣어두면 개인정보 민감쓰)

토큰 체크 뷰 - 인가된 사용자인지 확인

이제 HTTP로 응답해준 토큰을 FE개발자가 사용자의 HTTP request header에 잘 넣어줬다고 가정하고,

해당 토큰이 현재 페이지에서 활동할 수 있도록 인가된 토큰인지 확인 해주는 뷰를 만들어야 한다.

먼저 TokenCheckView(예제이름) 에서는 요청으로 넘어온 토큰 정보를 기존에 토큰을 발행할 때 적용했던 SECRET_KEY와 알고리즘 정보를 넣어서 디코딩해준다.

(요청으로 넘어온 토큰이 정상 발행된 토큰일 때) 디코딩을 하면 토큰을 발행할 때 넣었던 유저 정보가 반환된다.

이 유저 정보가 DB에 있는 정보와 일치하다면 200!

아니라면 403!

토큰을 매번 다 확인하나?

그 비밀은 HTTP 리퀘스트 헤더에 있다. 아래 두 글에서 살펴봤듯이 로그인에 성공한 사용자에게 토큰을 발행하고, 이 토큰을 새로운 요청 때마다 HTTP 리퀘스트 헤더에 넣어서 보내주면, 로그인된 상태로 유지시켜준다. 이때 이 토큰이 유효한 토큰인지 확인하기 위해 로그인 인증 데코레이터를 사용하려한다.

로그인 인증 데코레이터 만들기

데코레이터라 하면 특정 함수의 바로 위에 붙어, 특정 함수가 실행되기 전에 먼저 실행되어, 사전에 하고 싶은 작업을 처리하는 함수라고 할 수 있다. 우리는 이 데코레이터를 만들어 사용자가 특정 기능을 수행하기 전에 사용자가 그 기능을 수행할 권한이 있는지 살펴 볼 예정이다. 토큰으로 권한이 확인되지 않는다면 특정 기능을 담은 함수는 실행되지 않는다.

utils.py라는 파일을 만들어 작성했다. 하나의 앱에만 쓸게 아니라 데코레이터가 사용될 모든 함수에 공통으로 적용시켜야하기 때문이다. 필수는 아니다.

나는 my_settings.py (gitignore로 숨김 <-- 내 데이타베이스 정보, SECRET_KEY, ALGORITHM 타입 정보가 들어있다.) 그래서 github 에서 내 코드를 봐도 위 3가지는 알 수 없다.

def login_decorator(func):
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.headers.get('Authorization', None)          [1]
            payload = jwt.decode(access_token, SECRET_KEY, algorithm='HS256')  [2]
            user = Account.objects.get(email=payload['email'])                 [3]
            request.user = user                                                [4]

        except jwt.exceptions.DecodeError:                                     [5]
            return JsonResponse({'message' : 'INVALID_TOKEN' }, status=400)

        except Account.DoesNotExist:                                           [6]
            return JsonResponse({'message' : 'INVALID_USER'}, status=400)

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

    return wrapper

[ 1 ] : access_token은 HTTP Request의 헤더인 Authorization의 값을 가져오고, 없으면 None으로 넘긴다.
[ 2 ] : payload는 토큰을 디코딩하면 나오게 될 사용자에 대한 정보. 토큰 발행 시와 동일한 사용자라면 동일한 payload가 반환된다. 디코딩에 들어가는 SECRET_KEY와 algorithm은 토큰 발행 시 넣었던 정보와 같아야한다.
[ 3 ] : DB의 Account 테이블에서 [토큰을 디코딩 해서 나온 사용자 정보]와 매칭되는 사용자 정보를 불러오고, user라는 변수에 저장한다.
[ 4 ] : HTTP에서 받은 Request에 user변수를 저장해 데코레이터 다음에 나오는 함수에서 사용한다. 토큰 정보를 확인하는 HTTP Request 에는 토큰을 제외하고는 사용자 정보가 들어오지 않기 때문에, 이 user 값을 저장해서 이후 활용한다.
[ 5 ] : 없는 토큰 값이 들어왔을 경우 DecodeError를 처리한다.
[ 6 ] : Account 테이블에 매칭되는 값이 없을 경우 DoesNotExist를 처리한다.
-> import 한건 ObjectDoesNotExist 지만 이렇게 쓰면 Attribute Error 가 난다.

적용해보기 (예제)

댓글에는 사용자 이메일과 댓글 내용이 담기는 기본적인 기능으로 구성되어 있다. 기존에 작성된 코드에 GET과 POST를 실행하기 전에 로그인 토큰을 확인하도록 데코레이터를 추가하였다.

import json

from .models import Comment
from core.utils import login_decorator                       [1]

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

class CommentView(View):
    @login_decorator                                         [2]
    def get(self, request):
        return JsonResponse({'comment_list':list( Comment.objects.values())},status=200)

    @login_decorator                                         [3]
    def post(self, request):
        data = json.loads(request.body)

        Comment(
            email = request.user.email,                      [4]
            comment = data['comment'],
        ).save()

        return HttpResponse(status=200)

[ 1 ] : 데코레이터가 만들어진 곳에서 데코레이터를 임포트 해와야 사용할 수 있다.
[ 2 ] : get 메서드가 실행되기 전에 로그인 인증 여부를 확인한다.
[ 3 ] : post 메서드가 실행되기 전에 로그인 인증 여부를 확인한다.
[ 4 ] : 토큰을 디버깅해서 얻은 유저 이메일 정보를 활용한다.

profile
🇰🇷🇺🇸 #Back-End Engineer

0개의 댓글