Writable Nested Serializer로 중첩된 객체 한 번에 생성/수정하기

DRF의 기본 ModelSerializer는 중첩된 관계에 대해 읽기 전용으로 동작하는데, create()와 update() 메서드를 오버라이드하여 단일 API 호출로 부모-자식 객체를 원자적으로 생성/수정하는 방법

문제 상황과 해결책

기존 비효율적인 방식

  • 게시글 생성 후 태그를 별도로 연결하는 다중 API 호출

  • 네트워크 오버헤드와 데이터 정합성 문제 발생

해결책

  • 단일 API 호출로 중첩된 객체들을 한 번에 처리

  • 클라이언트 로직 단순화와 성능 향상

구현 원리

핵심 메커니즘

  • 부모 Serializer의 create()update() 메서드 오버라이드

  • validated_data.pop()으로 중첩 데이터 분리

  • 부모 객체 생성 후 자식 객체들과 관계 설정

1:N 관계 구현 (ForeignKey)

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    @transaction.atomic
    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        
        for track_data in tracks_data:
            Track.objects.create(album=album, **track_data)
        
        return album

M:N 관계 구현 (ManyToMany)

class PostSerializer(serializers.ModelSerializer):
    tags = TagSerializer(many=True)

    @transaction.atomic
    def create(self, validated_data):
        tags_data = validated_data.pop('tags')
        post = Post.objects.create(**validated_data)
        
        tag_list = []
        for tag_data in tags_data:
            tag, created = Tag.objects.get_or_create(name=tag_data['name'])
            tag_list.append(tag)

        post.tags.set(tag_list)
        return post

실무 핵심 팁

필수 사항

  • @transaction.atomic 데코레이터로 데이터 정합성 보장

  • get_or_create 활용으로 중복 데이터 방지

  • ViewSet에서 prefetch_related 사용으로 N+1 쿼리 문제 방지

복잡도 관리

  • 로직이 복잡해지면 서비스 계층 분리 고려

  • 과도한 중첩보다는 별도 엔드포인트 제공도 고려

성능 최적화

  • bulk_create 활용으로 대량 데이터 처리 최적화

  • 필요한 데이터를 미리 한 번의 쿼리로 조회

Last updated