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 대체 (위에 거 이거 하면서 났음)
- views.py (.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 |