프로젝트

Django 회원가입 및 회원정보수정 시 Serializer 분리

content0474 2025. 1. 6. 20:48

1. 오류 내용
회원가입과 회원정보수정을 동일한 Serializer를 사용하여 처리하려다 KeyError: 'password2' 오류 발생

오류 메시지

KeyError at /accounts/update/
'password2'

 

2. 오류가 생긴 이유
회원가입에서는 password와 password2 두 필드를 받아 비밀번호 일치 여부를 확인하는 로직이 포함되어 있음
하지만 회원정보수정에서는 password2 필드가 필요 없으며, 클라이언트가 이를 전달하지 않음
같은 Serializer를 사용하면서 password2 필드가 전달되지 않을 경우 validate 메서드에서 KeyError가 발생

문제의 근본 원인
회원가입과 회원정보수정의 요구사항이 다르다.

회원가입: password, password2로 비밀번호 검증 필수
회원정보수정: 비밀번호 검증이 필요하지 않고, 필드 업데이트만 진행

 

하나의 Serializer로 두 가지 기능을 처리하려다 오류가 발생

 

3. 수정하기 전 코드
serializers.py

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)
    groups = serializers.PrimaryKeyRelatedField(many=True, queryset=Group.objects.all(), required=False)
    user_permissions = serializers.PrimaryKeyRelatedField(many=True, queryset=Permission.objects.all(), required=False)

    class Meta:
        model = User
        fields = "__all__"
        extra_kwargs = {
            'email': {'required': True},
            'first_name': {'required': True},
            'last_name': {'required': True},
            'nickname': {'required': True},
            'birthday': {'required': True},
            'phone_number': {'required': True},
            'is_active': {'required': False, 'default': True},
        }

    def validate(self, data):
        if data['password'] != data['password2']:
            raise serializers.ValidationError({"password": "비밀번호가 일치하지 않습니다."})
        return data

    def create(self, validated_data):
        validated_data.pop('password2')
        password = validated_data.pop('password')
        user = User(**validated_data)
        user.set_password(password)
        user.is_active = True
        user.save()
        return user



views.py

class SignupView(APIView):
    def post(self, request):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)


class UpdateUserView(UpdateAPIView):
    permission_classes = [IsAuthenticated]

    def put(self, request):
        user = request.user
        serializer = UserSerializer(user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



4. 수정한 코드
serializers.py

회원가입용 Serializer

class RegisterUserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
    password2 = serializers.CharField(write_only=True, required=True)
    groups = serializers.PrimaryKeyRelatedField(many=True, queryset=Group.objects.all(), required=False)
    user_permissions = serializers.PrimaryKeyRelatedField(many=True, queryset=Permission.objects.all(), required=False)

    class Meta:
        model = User
        fields = "__all__"
        extra_kwargs = {
            'email': {'required': True},
            'first_name': {'required': True},
            'last_name': {'required': True},
            'nickname': {'required': True},
            'birthday': {'required': True},
            'phone_number': {'required': True},
            'is_active': {'required': False, 'default': True},
        }

    def validate(self, data):
        if data['password'] != data['password2']:
            raise serializers.ValidationError({"password": "비밀번호가 일치하지 않습니다."})
        return data

    def create(self, validated_data):
        validated_data.pop('password2')
        password = validated_data.pop('password')
        user = User(**validated_data)
        user.set_password(password)
        user.is_active = True
        user.save()
        return user



회원정보수정용 Serializer

class UpdateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['email', 'first_name', 'last_name', 'nickname', 'birthday', 'phone_number']

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance



views.py

class SignupView(APIView):
    def post(self, request):
        serializer = RegisterUserSerializer(data=request.data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)


class UpdateUserView(UpdateAPIView):
    permission_classes = [IsAuthenticated]

    def put(self, request):
        user = request.user
        serializer = UpdateUserSerializer(user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



5. 수정 후 효과
요구사항에 맞는 Serializer 분리
RegisterUserSerializer: 회원가입 시 비밀번호 검증 로직 포함
UpdateUserSerializer: 회원정보수정에 필요한 필드만 처리


가독성 및 유지보수성 향상
각 기능에 맞는 Serializer를 분리하여 코드가 명확해짐


오류 방지
회원정보수정 시 불필요한 password2 필드를 요구하지 않아 KeyError 문제를 해결