[ PROJECT ] Westgram endpoint 구현 (4) - 마무리 리뷰!

0. 변수명은 간단하게!

class User(models.Model):
     user_name   = models.CharField(max_length=30, unique=True)
     			-> 이렇게 해주면 무조건 unique하게 입력되기 때문에 
                	추후에 회원가입 시 무조건 unique하게 입력되고, 
                    	로그인 시에도 filter(), get()메서드를 사용했을 때 unique한 값만 가져올 수 있다. 

👆🏻 models.py에서 테이블을 정의할 때, 불필요하게 변수명을 길게 작성한 나..!

user = User.objects.get(id=1)
user.name

👆🏻 추후에 views.py에서 로직을 작성할 때, model 내 각 클래스에서 해당 필드의 값을 가져올 때, 불필요하게 user.user_name이라고 쓰는 것보다는 user.name 이렇게 적는 것이 더 가독성이 좋다!

🤩 수정한 모습

from django.db import models

class User(models.Model):
     name   = models.CharField(max_length=30, unique=True, default='')
     password    = models.CharField(max_length=100)

     class Meta:
         db_table = 'users'


1. json decode 에러 방지하기

def post(self, request):
        data = json.loads(request.body)
        try:
        	.....

이런 식으로 작성을 하면, 만에하나 프론트엔드에서 보내준 데이터가 없어서 json이 load할 데이터가 없다면

raise Exceptions ...... 

👆🏻요로케 decode 에러가 raise된다.

def post(self, request):
    try:
	data = json.loads(request.body)
       	.....
    except JsonDecodeError:
    	.....

이런식으로 만에하나 에러날 수도 있는 상황을 try문 안에 넣어주고,
구체적으로 except 상황을 명시해 줄 것 (물론 decode에러 외에 다른 에러들도 명시해줘야 한다..!)

🤩 수정한 모습

import json
import re
import bcrypt
import jwt

from django.http        import JsonResponse
from django.views       import View
from user.models        import User
from westagram.settings     import SECRET_KEY, ALGORITHM
# sign up
class Signup(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            if data.get('name') is not None:
                if data.get('password') is not None:
                    if User.objects.filter(name=data['name']).count() > 0:
                        return JsonResponse({'message':'DUPLICATED_USERNAME'}, status=400)
                    if re.search('[0-9]+', data['password']) is None:
                        return JsonResponse({'message':'TOO_SHORT_PW'}, status=400)
                    
                    hashed_password = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt())
                    User.objects.create(
                        name   = data['name'],
                        password    = hashed_password.decode('utf-8')
                    )
                    return JsonResponse({'message':'SUCCESS'}, status=200)
        except KeyError:							<- 요 구간!
            return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except ValueError:  
            return JsonResponse({'message':'DECODE_ERROR'}, status=400)

2. 간결한 if문

    def post(self, request):
        try:
            data = json.loads(request.body)
            if data.get('user_name') is not None:

이렇게 구구절절 써둔 나의 if문...
하지만 json형식으로 들어온 data 변수는 이미 dictionary형식이므로
.get()메서드 사용 시 값이 없으면 무조건 None을 반환하기 때문에
굳이 if문을 저렇게 길게 작성할 필요가 없다!

그리고 data에서 가져온 값들을 따로 변수에 담아두지 않으면
계속해서 다시 상단으로 돌아가서 data 변수 내의 값들을 확인하기 때문에,
자주 쓰이는 값들은 변수에 담아서 준비해 두는 것이 좋다! (가독성 측면에서도..!)

name = data.get('name')
password = data.get('password')
if name or password 👍 :
    return ...

👆🏻 요로케만 작성해주어도 되는 것!!!


3. filter()로 DB 내 데이터 존재여부 확인하기

if name:
    if User.objects.filter(name=name).exists():
        return ...
    if len(password) < 5:
        return ...

이미 filter()라는 메서드는 해당하는 값을 하나만 가져오기 때문에 굳이 내가 했었던 것 처럼 .count()메서드로 개수를 세어줄 필요가 없다..!
그저 가져온 값이 존재하는지, 존재하지 않는지만 보면 되는 것..!

🤩 수정한 모습

class Signup(View):
    def post(self, request):
        try:
            data        = json.loads(request.body)
            name        = data.get('name')
            password    = data.get('password')
            
            if name:
                if password:
                    if User.objects.filter(name=name).exists():
                        return JsonResponse({'message':'DUPLICATED_USERNAME'}, status=400)
                    if len(password) < 8:
                        return JsonResponse({'message':'TOO_SHORT_PW'}, status=400)
                    
                    hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
                    User.objects.create(
                        name        = name,
                        password    = hashed_password.decode('utf-8')
                    )
                    return JsonResponse({'message':'SUCCESS'}, status=200)
        except KeyError:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except ValueError:  
            return JsonResponse({'message':'DECODE_ERROR'}, status=400)

4. 상황에 맞는 status 코드!

status code!

  • 200
    : data get, post, update 처리를 성공하였을 때
  • 201
    : 데이터resource가 생성된 경우
  • 400
    : front-end의 요청에 대한 처리 불가할 때
    : bad request
  • 401
    : 권한이 요구되지 않았을 때

5. 지저분한 except 처리

                else:
                    return JsonResponse({'message':'KEY_ERROR'}, status=400)
            else:
                return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)

모든 if문에 대한 예외처리를 각각의 except로 통일한 내 코드!
아무리 생각해도 이것보다 깔끔하게 처리를 할 수 있을 것 같은데... 궁금하던 찰나!

        except KeyError:
            return JsonResponse()
       except User.DoesNotExist:
           return JsonReponse()
       except User.MutipleObjectReturn:
          return JsonResponse({'message' :'DUPLICATE_USER'}, status=409)

👆🏻 어짜피 저 모든 상황에서 날 예외는 key error이기때문에, except에 구체적으로 어떤 에러에 대한 상황인지 명시해주면
하나의 except여도 잘 처리할 수 있다!



최종 views.py 로직(회원가입, 로그인)

🤯 🥺 🤩

import json
import re
import bcrypt
import jwt

from django.http        	import JsonResponse
from django.views       	import View
from user.models        	import User
from westagram.settings     	import SECRET_KEY, ALGORITHM
# sign up
class Signup(View):
    def post(self, request):
        try:
            data        = json.loads(request.body)
            name        = data.get('name')
            password    = data.get('password')
            
            if name:
                if password:
                    if User.objects.filter(name=name).exists():
                        return JsonResponse({'message':'DUPLICATED_USERNAME'}, status=400)
                    if len(password) < 8:
                        return JsonResponse({'message':'TOO_SHORT_PW'}, status=400)
                    
                    hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
                    User.objects.create(
                        name        = name,
                        password    = hashed_password.decode('utf-8')
                    )
                    return JsonResponse({'message':'SUCCESS'}, status=200)
        except KeyError:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except ValueError:  
            return JsonResponse({'message':'DECODE_ERROR'}, status=400)

# sign in
class Signin(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            name        = data['name']
            password    = data['password']
            
            user_info = User.objects.get(name=name)
            signin_id = user_info.name
            signin_pw = user_info.password.encode('utf-8')
            
            if User.objects.filter(name=name).exists():
                if signin_id == name and bcrypt.checkpw(password.encode('utf-8'), signin_pw):
                    access_token = jwt.encode({'name':name}, SECRET_KEY, algorithm = ALGORITHM).decode('utf-8')
                    return JsonResponse({'message':'SUCCESS', 'access_token':access_token}, status=200)
        except KeyError:
            return JsonResponse({'message':'KEY_ERROR'}, status=401)    
        except ValueError:
            return JsonResponse({'message':'INVALID_USER'}, status=401)
        except User.MultipleObjectsReturned:
            return JsonResponse({'message':'MULTIPLE_USERS'}, status=409)            

사실 따지고보면django.core.exceptionsMultipleObjectsReturned는 사용하지 않았으니까 제거하는게 맞았는데..
exception을 사용하려고 해봤더니 자꾸 model에서 에러가 난다고 하는 것이 아닌가..!
공식 문서를 확인한 결과, model class에서 사용하는 것이라고 한다. 👉🏻 Model Class Reference
(model class에 import 하고 views.py에서 exception MultipleObjectsReturned로 쓰는 것이었음!)


오늘의 평화로운 코딩 끗!
(현재시각 새벽 4:35분 휴..)

좋은 웹페이지 즐겨찾기