[1st Project] Brokurly?! ver 1.5

제갈창민·2021년 12월 15일
0

작업물

목록 보기
6/9
post-thumbnail

1. 유효성 검사 모듈화(validator.py)

  • project 에서 맡은 부분중 하나인데, 사실상 부록정도의 난이도이다. (한번 해봤던거라 쉽게 넘어갈줄 알았는데 '아니어따')
import re

from django.core.exceptions import ValidationError


REGEX_EMAIL    = '^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
REGEX_PASSWORD = '^.*(?=^.{8,15}$)(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&+=]).*$'
REGEX_USERNAME = '^[a-zA-Z0-9]{6,16}$'
REGEX_CONTACT  = '^\d{2,3}-\d{3,4}-\d{4}$'

def validates_email(email):
    if not re.match(REGEX_EMAIL, email) or email is None:
        raise ValidationError('Invalid Email')

def validates_password(password):
    if not re.match(REGEX_PASSWORD, password) or password is None:
        raise ValidationError('Invalid Password')

def validates_username(username):
    if not re.match(REGEX_USERNAME, username) or username is None:
        raise ValidationError('Invalid Username')

def validates_contact(contact):
    if not re.match(REGEX_CONTACT, contact) or contact is None:
        raise ValidationError('Invalid Contact')

-> 정규표현식은 마치 수학공식처럼, 고고학자가 고대의 언어를 파헤치듯, 암호해석가에 빙의해서 만들어야 하는 것 같다.
-> 내 손으로 만들어낸 짧은 USERNAME과 CONTACT 정규표현식을 제외한 이메일과 패스워드는 다른 분들이 만든것을 가져왔다.
-> 그런데, contact가 끝까지 통과하지 못해서 팀원들과 논의 끝에 삭제키로 했다. (위는 삭제전 코드)
-> 사실 모듈화해서 import만 잘해오면 마무리 되는 부분이기에 손풀기 느낌으로 30여분 만에 만들어냈다.
(추가) : 비밀번호가 검사를 통과하지 못했다. 어떤 형태로 입력해도(맞든 틀리든) 에러가 raise 된다.
(해결) : 정규표현식을 연습할 수 있는 사이트에서 간단한 형식의 표현식을 새로 만들어 적용시켰고, 통과되었다.
(추가2) : contact도 하이픈이 있든 없든 검사를 통과하지 못했다. 팀원들과 긴급회의를 통해 더 시간을 뺏길 수 없으니 검사항목에서 삭제키로 했다.

2. 토큰 인증 decorator 함수

  • 해본 적없고 어렵기로 악명높은 decorator 를 자진해서 맡기로 했다. 해본 적 없는 기능에 도전하는 것이 이번 프로젝트의 목표였기에, 어렵지만 사용빈도가 아주 높은 이녀석을 그냥 넘길 수가 없었다.
import jwt

from django.http		import JsonResponse
from .settings 			import SECRET_KEY, ALGORITHM

def sign_in(func):
    def decorator(self, request, *args, **kwargs):
        try:
            # 토큰 받아 오기(헤더)
            # jwt 디코딩
            # 토큰 틀렸을 때 에러(조건문)
            # return func()
        except
    return decorator

1) 여기까지는 많은 예제들을 통해 눈팅으로 써놨지만, 여기부터가 시작이었다. 일단 try - (func) - except 순으로 decorator 가 발동될테니, token 부터 받아와야 했다.

try:
    access_token = request.headers.value('token')

2) 그리고 받아온 토큰을 jwtdecode 하고, 해당 token을 갖고 있는 User의 정보를 가져온다.
이때, 유저의 정보를 찾는 기준은 id 로 하는데, 간단히 말해서 보안상의 이유 때문이라고 한다.

try:
    access_token = request.headers.value('token')
    decode_token = jwt.decode('utf-8')(access_token, SECRET_KEY, ALGORITHM)
    request.user = User.objects.get(id=decode_token['id'])

-> request.user 가 원래는 confirmed_user 였는데, 구글링한 자료에서 이렇게 작성한것을 보고 '응? 무슨 의미지?' 하면서도 일단 완성하는 것에 급급했기에 그냥 넘어갔었다.
-> 이것이 화근이었다. 이 decorator를 만들고 나서 곧바로 프론트와 통신에 돌입했는데, 메인페이지에서 오류가 많아서 그걸 수정하느라 리뷰할 시간적 여유가 없었다. 결국, 다른 view에서 작업한 팀원이 request.user 를 변수로 한 코드와 충돌이 났고, 내가 미리 이런 사항을 알리지 못해서 해당 팀원이 자기 코드가 잘못된 줄 알고 많은 부분을 수정 하게 된 것이다.

(추가) 멘토님도 굳이 이렇게 쓸 필요 없다고 하셨다. 나중에 decorator를 가져다 쓰는 쪽에서 헷갈릴 수 있으니 바꾸는걸 추천하셨다.

3) tokenheaders에 없다면 Error를 뽑아내는 조건문을 추가했다.

try:
    access_token = request.headers.value('token')
    decode_token = jwt.decode('utf-8')(access_token, SECRET_KEY, ALGORITHM)
    request.user = User.objects.get(id=decode_token['id'])
        
    if 'token' not in request.headers:
        return JsonResponse({'message':'토큰 없는데용'}, status=404)
        
    return func(self, request, *args, **kwargs)

4) ID 를 잘못 입력했을 시 DoesNotExist, 비밀번호를 잘못 입력했을 시 DecodeError 를 넣었다.
-> JSONDecodeError 이 에러는 json형식의 문자를 'UTF-8' 로 encode해주지 않았을 때 발생하는 에러인데, 이는 내가 실수하지 않으면 발생하지 않을 에러이다. 그래서 내 실수로 발생할 에러를 exception 으로 처리하는 것은 맞지 않기에, 최종 수정에서 제외했다.(여기에선 아직 존재함)

except User.DoesNotExist:
    return JsonResponse({'message':'Invalid User'}, status=400)

except DecodeError:
    return JsonResponse({'message':'Invalid Token'}, status=400)

except JSONDecodeError:
    return JsonResponse({'message':'JSONDecodeError'}, status=404)

5) 모든 코드들을 합친 최초의 decorator.py

import jwt

from django.http       import JsonResponse
from json.decoder      import JSONDecodeError
from jwt.exceptions    import DecodeError

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

def login_required(func):
    def wrapper(self, request, *args, **kwargs):
        try:
            access_token = request.headers['token']
            decode_token = jwt.decode('utf-8')(access_token, SECRET_KEY, algorithm=ALGORITHM)
            request.user = User.objects.get(id=decode_token['id'])

            if 'token' not in request.headers:
                return JsonResponse({'message':'Token not Exist'}, status=404)

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

        except User.DoesNotExist:
            return JsonResponse({'message':'Invalid User'}, status=400)

        except DecodeError:
            return JsonResponse({'message':'Invalid Token'}, status=400)

        except JSONDecodeError:
            return JsonResponse({'message':'JSONDecodeError'}, status=404)
        
    return wrapper

3. Review

-> 우선, 소통의 소홀 에 대해 반성했다. 데코레이터가 다 준비됐음에도 팀 노션페이지나 트렐로에 업데이트 하지 않고 구두로만 완성됐다고 알렸고, 다른 작업에 몰두했었다. 해당 팀원도 코딩에 몰두 하고 있던 바람에 둘 다 업데이트 하지 않았고 다른 팀원들은 우리가 말하기 전까지 기다리고 있었던 것이다. 팀원의 블로커를 늘려버린 셈.
또, 다른 팀원과 연계해야하는 데코레이터를 만들었음에도 팀원과 함께 리뷰과정을 거치지 않았다. 시간이 촉박했다는 핑계를 댈 수야 있겠지만, 리뷰를 하지 않음으로써 발생할 수 있는 에러와 오류들에 대한 추가 수정 작업에 시간이 더 들 수 있다는 생각을 했다면 빠뜨리는 일은 없었을 것이다.
-> 그 다음부터는 정말 사소한 사항(심지어 당일 식사메뉴도 미리 알렸음)도 그때그때 트렐로에 업데이트하고 당사자에게 곧바로 알리거나 숏미팅을 통해 바로 해결하는 등 소통에 온 신경을 쏟았다.

profile
자기계발 중인 신입 개발자

0개의 댓글