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