728x90
반응형

알고리즘


  • 에러는 아니고 프로그래머스 피자나눠먹기(3)푸는데 (2)처럼 최소공배수인데 걍 피자 개수가 정해진 게 아니고 변수로 바뀌었을 뿐이네 하고 푸는데 계속 안 되길래 포기하고 오늘 풀었는데 아닝 ㅣ걸 왜 최소공배수 문제로 봤지??? 새벽 4시가 넘어가서 정신이 없었나보다. 사내 복지 좋은 데로 가야지… 안 그럼 정신 나가서 이상한 에러만 내고 있을 것ㄱ ㅏㅌ다.

encode() argument ‘errors’ must be str, not bytes


  • 발생 이유 : 회원가입할 때 db에 패스워드 저장 시 디코딩을 안 하고 넣어서byte타입으로 들어감
# before
def post(self, request) :
    serializer = UserSerializer(data=request.data)
    if serializer.is_valid() :
        hashed_password = bcrypt.hashpw(request.data["password"].encode("utf-8"), bcrypt.gensalt())
        serializer.save(password = hashed_password)
        return Response({"message":"가입완료!"}, status=status.HTTP_201_CREATED)
    else : 
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# after
def post(self, request) :
    serializer = UserSerializer(data=request.data)
    if serializer.is_valid() :
        hashed_password = bcrypt.hashpw(request.data["password"].encode("utf-8"), bcrypt.gensalt())
        serializer.save(password = hashed_password.decode("utf-8"))
        return Response({"message":"가입완료!"}, status=status.HTTP_201_CREATED)
    else : 
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • 알게 된 것 : 해싱한 암호는 byte 타입인데, db에 그대로 넣으면 그 형태 그대로 문자열로만 변환되어 저장된다. 이걸 또 다시 불러와서 인코딩하면 byte 타입 형태의 문자열을 또 byte타입으로 변환한 값이 나오기 때문에, 데이터 베이스에 저장할 때엔 디코딩된 문자열 타입의 데이터를 저장하고, .checkpw로 확인할 때 해당 데이터를 인코딩 해야 한다.

.set_password 대체 (위에 거 이거 하면서 났음)


from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from rest_framework.generics import get_object_or_404
from users.models import User
from rest_framework.permissions import IsAuthenticated
from users.serializers import UserSerializer, ProfileEditSerializer
import json, bcrypt, jwt
from django.core.exceptions import ValidationError
from OIL_PAINTING_BE.settings import SECRET_KEY, ALGORITHM


class UserView(APIView):
    def post(self, request) :
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid() :
            hashed_password = bcrypt.hashpw(request.data["password"].encode("utf-8"), bcrypt.gensalt())
            serializer.save(password = hashed_password.decode("utf-8"))
            return Response({"message":"가입완료!"}, status=status.HTTP_201_CREATED)
        else : 
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
                
    def delete(self, request):
        user = get_object_or_404(User, id=request.user.id)
        if user:
            user.delete()
            return Response({"message":"지금까지 저희 서비스를 이용해 주셔서 감사합니다."}, status=status.HTTP_200_OK)
        return Response({"message":"이런... 탈퇴에 실패하셨습니다."}, status=status.HTTP_400_BAD_REQUEST)



class LoginView(APIView) :
    def post(self, request) :
        try_email = request.data["email"]
        try_password = request.data["password"]
        user = get_object_or_404(User, email = try_email)
        
        if not user :
            raise ValidationError({"INVALID_EMAIL" : "존재하지 않는 이메일입니다."})
        
        validating_password = bcrypt.checkpw(try_password.encode("utf-8"), user.password.encode("utf-8"))
        
        if not validating_password :
            raise ValidationError({"password" : "틀린 비밀번호입니다."})
        
        access_token = jwt.encode({"id": user.id}, SECRET_KEY, algorithm=ALGORITHM)
        
        def decoding_access_token(access_token, SECRET_KEY) :
            payload = jwt.decode(access_token, SECRET_KEY, algorithms=ALGORITHM)
            return payload
        
        user_info = decoding_access_token(access_token, SECRET_KEY)
        
        return Response({"message":f"환영합니다, {user_info}님!", "access_token" : access_token}, status=status.HTTP_200_OK)

- 알고리즘은 simpleJWT에 잇던 HS512를 그대로 썼다. 기왕 직접 구현해보는 거 다른 알고리즘을 써볼 걸 그랬나 하는 아쉬움이 남는다.

- serializers.py

from rest_framework import serializers
from users.models import User
import re

class UserSerializer(serializers.ModelSerializer):
    # 패스워드 확인은 serialization하지 않는다.
    password_check= serializers.CharField(error_messages={"write_only":True,'required':'비밀번호 확인까지 입력해 주세요.', 'blank':'비밀번호 확인까지 입력해 주세요.'}) 

    class Meta:
        model = User
        fields = ("email","nickname","password", "password_check")
        extra_kwargs = {
            "email" :{"error_messages" : {
                "required" : "이메일을 입력해 주세요.",
                "blank" : "이메일을 입력해 주세요."}},
            "nickname" : {"error_messages":{
                "required":"닉네임을 입력해 주세요.",
                "blank":"닉네임을 입력해 주세요."}},
            "password" : {"write_only":True, # password도 직렬화하지 않는다.
                          "error_messages":{
                "required":"비밀번호를 입력해 주세요.",
                "blank":"비밀번호를 입력해 주세요."}},
        }

    def validate(self, validated_data) :
        email = validated_data.get("email")
        email_reg = r'^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
        password_reg = r"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{5,}$"
        password = validated_data.get("password")
        password_check = validated_data.get("password_check")

        # 이메일 유효성 체크
        if not re.search(email_reg, str(email)) :
            raise serializers.ValidationError(detail={"email":"이메일 형식에 맞춰서 작성해 주세요."})

        # 비밀번호 유효성 체크
        if not re.search(password_reg, str(password)) :
            raise serializers.ValidationError(detail={"password":"최소 한 개의 영문자와 숫자를 포함해 5글자 이상으로 만들어 주세요."})
        elif password != password_check :
            raise serializers.ValidationError(detail={"password":"동일한 비밀번호를 입력해 주세요."})

        return validated_data


    def create(self, validated_data):
        email = validated_data["email"]
        nickname = validated_data["nickname"]
        password = validated_data["password"]
        user = User(
            email = email,
            nickname=nickname,
            password = password
            )
        user.save()
        return user
    
    def update(self, validated_data):
        email = validated_data["email"]
        nickname = validated_data["nickname"]
        password = validated_data["password"]
        user = User(
            email=email,
            nickname=nickname,
            password = password
            )
        user.save()
        return user
  • 느낀점 : 모듈이란 게 얼마나 강려크한 친구인지… 알게 되었달까…. 프젝 끝나면 refresh token까지만 해보고 여기서 멈춰야겠다. 그래도 set_password가 돌아가는 방식을 알게 돼서 좋았다. 뭐든 알아두면 언젠가는 도움이 될 날이 오겠지. 지금 당장은 아니더라도.
  • 그리고 회원가입 되고, 로그인 되고, access token 잘 뱉어내서 일단 됐다고 판단은 했는데… 이렇게 하는 건지는 모르겠다. 혼자 찾아서 구현해볼 때 이런 점이 어렵다. 얘가 맞게 구현이 된 건지, 아니면 돌아가긴 하는데 코드가 안 되느니만 못한 코드인지 알 길이 없다.
반응형

'Programming > TIL and WIL' 카테고리의 다른 글

221128 TIL 겸 유화 제작 프로젝트 KPT 회고  (0) 2022.11.28
🐻 221124 Today I Learned 🐻  (0) 2022.11.24
🐻 221122 Today I Learned 🐻  (0) 2022.11.22
🐻 221121 Today I Learned 🐻  (0) 2022.11.21
WIL  (0) 2022.11.21

+ Recent posts