서론
serializers.py
views.py
urls.py
회고
이번 글에서는 사용자 로그인 기능을 구현해보겠습니다.
사용자로부터 이메일 주소, 비밀번호를 입력받아 이를 검증하고, 정보가 올바르다면 로그인하는 LoginSerializer를 구현해보겠습니다.
from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed
from .models import *
from django.contrib.auth import get_user_model, authenticate
from django.contrib.auth.password_validation import validate_password
from authentication.models import User
...
class LoginSerializer(serializers.ModelSerializer):
username = serializers.CharField(max_length = 255, read_only = True)
email = serializers.EmailField()
password = serializers.CharField(max_length = 128, write_only = True)
def validate(self, data):
email = data.get("email", None)
password = data.get("password", None)
# 검증 과정에서 에러 발생 시 ValidationError 반환
if email is None:
raise serializers.ValidationError("이메일 주소를 입력해주십시오.")
if password is None:
raise serializers.ValidationError("비밀번호를 입력해주십시오.")
user = authenticate(username = email, password = password)
# 인증 과정에서 에러 발생 시 AuthenticationFailed 반환
if user is None:
raise AuthenticationFailed("이메일 주소 혹은 비밀번호가 잘못 입력되었습니다.")
if not user.is_active:
raise AuthenticationFailed("유효하지 않은 계정입니다.")
return {"email": user.email, "username": user.username}
class Meta:
model = get_user_model()
fields = ["username", "email", "password"]
username 대신 email을 사용해 로그인하기 때문에 username은 read_only
password는 보안상의 이유로 출력하지 않기 위해 write_only
email 혹은 password가 없을 경우, 검증 과정에서 ValidationError 반환
authenticate() 메서드는 email, password의 조합이 DB에 매칭되는 결과가 존재하는지 확인하는 역할
인증(authenticate) 과정에서 에러 발생 시 AuthenticationFailed exception 반환
로그인 성공 시 email, username 정보 반환
위에서 구현한 serializer를 기반으로 사용자에게 보여줄 뷰를 구현해보겠습니다. 클래스 기반의 뷰를 구현할 때, generics 클래스를 상속하면 편리하기 때문에 이를 시도해봤으나, 다른 뷰들과 달리 Login 뷰는 불가능한 것 같아서 APIView를 상속해 구현했습니다.
from rest_framework import generics, status
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from .models import *
from .serializers import *
from authentication.models import User
from authentication.renderers import UserJsonRenderer
...
class Login(APIView):
permission_classes = (AllowAny, )
serializer_class = LoginSerializer
renderer_classes = (UserJsonRenderer, )
def post(self, request):
user = request.data
serializer = self.serializer_class(data = user)
serializer.is_valid(raise_exception = True)
return Response(serializer.data, status = status.HTTP_200_OK)
모든 사용자가 로그인을 시도할 수 있도록 permission은 AllowAny
사용자로부터 데이터를 입력받기 위해 post() 메서드 구현
입력받은 데이터가 유효한 경우, serializer의 데이터와 HTTP 200(OK) 반환(로그인 처리)
위에서 구현한 뷰를 웹 상에서 접근이 가능하도록 urls.py에 등록해보겠습니다.
from django.urls import path
from .views import *
urlpatterns = [
...
path('login', Login.as_view()),
]
Postman을 사용해 올바른 이메일 주소와 비밀번호 쌍을 입력하면 로그인이 성공하는지 테스트해보겠습니다.
위와 같이 로그인에 성공해 HTTP 200과 사용자 정보를 반환하는 것을 확인할 수 있습니다.
이번에는 Postman을 사용해 올바르지 않은 이메일 주소와 비밀번호 쌍을 입력하면 로그인이 실패하는지 테스트해보겠습니다.
위와 같이 로그인에 실패해 HTTP 403과 함께 에러 메시지를 반환하는 것을 확인할 수 있습니다.
chatGPT에 물어봐가며 LoginSerializer를 구현했는데, 모델에 직접적으로 연관된 serializer가 아니기 때문에 Meta 클래스가 없어도 괜찮다는 답변을 받아서 이를 바탕으로 수정했을 때 Meta 클래스를 찾을 수 없다는 에러가 발생했습니다. 이후에는 model 파라미터가 누락된 Meta 클래스를 제시해 다른 에러가 발생하는 등, 지속적으로 틀린 답변을 받아서 문제 해결에 어려움을 겪었습니다.
결국에는 구글링과 여러 번의 시도 끝에 문제를 해결했고, chatGPT 등의 대화형 AI가 편리한 것은 부정할 수 없지만, 틀린 답변을 줄 수 있다는 것을 유의하며 사용해야 한다는 것을 실감했습니다. 또한, 결국 개발 과정에서 문제를 해결하기 위해서는 코드와 프로젝트의 구조를 스스로 잘 파악하고 있어야 한다는 것을 느꼈습니다.