get_serializer_class: 요청별로 다른 Serializer 동적 반환하기

하나의 ViewSet에서 요청의 종류(action, method, 권한 등)에 따라 각기 다른 Serializer를 동적으로 반환하는 방법

필요성

하나의 모델에 대한 API에서 상황별로 다른 데이터 형식이 필요한 경우:

  • 목록 조회 vs 상세 조회: 목록에서는 간단한 정보만, 상세에서는 모든 정보

  • 읽기 vs 쓰기: 조회 시 비밀번호 제외, 생성/수정 시 비밀번호 포함

  • 사용자 권한: 관리자에게는 민감 정보 포함, 일반 사용자에게는 제한된 필드만

동작 원리

DRF의 Serializer 결정 과정:

  1. Request 수신 → ViewSet action 실행

  2. Serializer 필요 시 get_serializer() 호출

  3. get_serializer()가 내부적으로 get_serializer_class() 호출

  4. 커스텀 구현된 get_serializer_class()가 조건에 맞는 Serializer 반환

구현 예제

action에 따른 분기

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.select_related('author').all()

    def get_serializer_class(self):
        if self.action == 'list':
            return PostListSerializer
        return PostDetailSerializer

HTTP 메서드에 따른 분기

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()

    def get_serializer_class(self):
        if self.request.method == 'GET':
            return UserReadSerializer
        return UserWriteSerializer

사용자 권한에 따른 분기

class SomeModelViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = SomeModel.objects.all()

    def get_serializer_class(self):
        user = self.request.user
        if user.is_authenticated and user.is_staff:
            return AdminUserSerializer
        return RegularUserSerializer

실무 팁

딕셔너리를 활용한 깔끔한 코드

class PostViewSet(viewsets.ModelViewSet):
    serializer_map = {
        'list': PostListSerializer,
        'retrieve': PostDetailSerializer,
        'create': PostCreateSerializer,
        'update': PostUpdateSerializer,
    }
    default_serializer_class = PostDetailSerializer 

    def get_serializer_class(self):
        return self.serializer_map.get(self.action, self.default_serializer_class)

API 문서화 도구 연동

drf-spectacular 사용 시 @extend_schema 데코레이터로 각 action별 Serializer 명시 필요

장점

  • 코드 재사용성: 관련 로직을 하나의 ViewSet으로 통합

  • 유연성: 다양한 조건으로 분기 가능

  • 깔끔한 URL 설계: URL 분리 없이 통일된 엔드포인트 사용

  • 향상된 보안: 읽기/쓰기 Serializer 분리로 민감 정보 노출 방지

Last updated