[PlanTo #4] 회원가입 및 검증

이원진·2023년 7월 17일
0

Planto

목록 보기
4/9
post-thumbnail
post-custom-banner

목차


  1. 서론

  2. serializers.py

  3. views.py

  4. urls.py

  5. renderers.py

0. 서론


이번 글에서는 지난 글에서 구현했던 커스텀 User Model을 기반으로 회원가입 기능과 검증 로직을 구현해보겠습니다.

1. serializers.py


serializer(직렬화)는 데이터의 구조를 유지한 채 다른 컴퓨팅 환경에서 사용 가능한 포맷으로 변환하는 것을 의미합니다. 즉, 파이썬 문법으로 작성된 클래스(High-Level)를 다른 환경에서도 사용할 수 있도록 JSON(Low-Level)의 형태로 변환하는 역할을 합니다.

Django는 풀스택 프레임워크이기 때문에 HTML form으로 데이터를 전달하므로 serializer 모듈의 기능이 약하지만, DRF는 이를 보완해 강력한 기능의 serializer 모듈을 지원합니다.

from rest_framework import serializers
from .models import *
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from authentication.models import User

class UserSerializer(serializers.ModelSerializer):
    # User의 Task 객체를 PrimaryKeyRelatedField로 연결
    tasks = serializers.PrimaryKeyRelatedField(many = True, queryset = Task.objects.all())

    class Meta:
        model = get_user_model()
        fields = ["username", "email", "tasks"]

class RegistrationSerializer(serializers.ModelSerializer):
    # django의 password validation 사용해 비밀번호 검증
    password = serializers.CharField(write_only = True, required = True, validators = [validate_password])
    password2 = serializers.CharField(write_only = True, required = True)

    token = serializers.CharField(max_length = 128, read_only = True)

    # 확인용 비밀번호 일치 여부 검증
    def validate(self, attrs):
        if attrs["password"] != attrs["password2"]:
            raise serializers.ValidationError({"password": "두 비밀번호가 일치하지 않습니다."})

        return attrs

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

    class Meta:
        model = get_user_model()
        fields = ["username", "email", "password", "password2", "token"]
  • serializer.ModelSerializer를 상속할 경우, 별도로 create(), update() 메서드를 정의하지 않고 Meta 클래스에 모델과 필드만을 명시해 간단하게 구현 가능

    • create(), method()를 정의하지 않을 경우 DRF의 기본 create(), update() 메서드를 사용

  • UserSerializer: 사용자 목록과 상세 페이지를 구현하기 위한 serializer

  • RegistrationSerializer: 사용자가 입력한 데이터를 검증하고, 이를 기반으로 회원가입 기능을 하는 serializer

    • password는 create(), update()에만 사용하고 serialize에는 배제하기 위해 write_only로 처리

    • django auth의 password validator를 사용해 password를 1차 검증 후, 확인용 비밀번호(password2)를 입력받아 2차 검증



2. views.py


위에서 구현한 serializer를 기반으로 사용자에게 보여줄 뷰를 구현해보겠습니다.

from rest_framework import generics
from .models import *
from .serializers import *
from authentication.models import User
from authentication.renderers import UserJsonRenderer

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class Registration(generics.CreateAPIView):
    queryset = User.objects.all()
    serializer_class = RegistrationSerializer
  • generic 클래스를 사용해 간결하게 클래스 기반 뷰 구현

    • get(), post() 등의 메서드를 구현하지 않아도 됨

  • UserList: 사용자 목록 뷰

  • UserDetail: 사용자 상세 정보 뷰

  • Registration: 회원가입 뷰



3. urls.py


위에서 구현한 뷰를 웹 상에서 접근이 가능하도록 urls.py에 등록해보겠습니다.

from django.urls import path
from .views import *

urlpatterns = [
    path('users', UserList.as_view()),
    path('users/<int:pk>', UserDetail.as_view()),
    path('register', Registration.as_view()),
]
  • views.py에 정의했던 뷰 클래스의 as_view() 메서드를 사용해 구현

  • 'users/<int:pk>': 사용자 상세 정보 url

    • User 모델의 int 타입 기본키(pk)를 사용

회원가입 테스트

이제 url을 사용해 뷰에 접근이 가능하므로, Postman이라는 API 플랫폼을 사용해 기능을 테스트해보겠습니다. Postman은 서버에 HTTP 요청을 보내고, 응답을 받아 보여주는 기능을 제공해 개발한 API를 쉽게 테스트할 수 있도록 도와줍니다.

서버를 실행한 뒤, 위의 사진과 같이 localhost:8000/register에 JSON 형식으로 POST 요청을 보냅니다.

그러면 아래와 같이 JSON으로 보낸 username과 email 사용하고, token을 만들어 새로운 사용자가 생성되는 것을 응답으로 확인할 수 있습니다.

검증 테스트

입력받은 데이터에 문제가 있을 경우에는 어떤 응답을 보내는지 확인해보겠습니다.

위의 사진과 같이 password를 특수문자 없이 "qwer1234"라는 흔한 문자열로 입력할 경우, 아래와 같이 비밀번호가 너무 흔하다는 경고를 출력하고 사용자를 생성하지 않는 것을 확인할 수 있습니다.



4. renderers.py


위에서 회원가입 요청에 대한 응답을 받을 때, 어떤 모델에 대한 데이터인지 확인하기 어렵다는 불편한 점이 있었고, 이를 renderer로 해결해보겠습니다. renderer는 응답 데이터를 원하는 형식으로 rendering하는 역할을 합니다.

import json

from rest_framework.renderers import JSONRenderer

class UserJsonRenderer(JSONRenderer):
    charset = "utf-8"

    def render(self, data, media_type = None, renderer_context = None):
        # data 내의 token은 Byte 타입으로 존재
        token = data.get("token", None)

        # Byte는 직렬화(Serialize)하지 못하기 때문에 rendering 전에 decode
        if token is not None and isinstance(token, bytes):
            data["token"] = token.decode("utf-8")

        # data를 "user"로 감싼 뒤, json 타입으로 render
        return json.dumps(
            {"user": data}
        )
  • serializer는 High-Level 형식의 데이터를 Low-Level의 형식으로 변환하는 것인데, Byte가 json보다 Low-Level이기 때문에 serialize가 불가능하므로 rendering 전에 decode

이제 구현한 renderer를 views.py에 적용해보겠습니다.

  • todo/views.py

    ...
    
    class Registration(generics.CreateAPIView):
        ...
        renderer_classes = (UserJsonRenderer, )
    • renderer_classes에 구현한 renderer 클래스 지정

    • renderer_classes = UserJsonRenderer와 같은 형식으로 지정할 경우, 아래와 같이 내부적으로 type 객체에 len() 함수를 사용해 에러가 발생

Renderer 테스트

위와 같이 renderer를 사용할 경우, 아래 사진과 같이 기존 데이터가 "user"라는 블럭으로 둘러쌓여 반환되므로 어떤 모델의 데이터인지 쉽게 식별할 수 있습니다.

post-custom-banner

0개의 댓글