DRF3 튜토리얼 3 - 클래스 기반 뷰

원문 - Class Based Views

번역을 허락해 준 Tom Christie에게 고마움을 전합니다.


튜토리얼 3: 클래스 기반 뷰

앞서 함수 기반으로 만들었던 API 뷰를 클래스 기반 뷰로도 만들 수 있습니다. 이는 일반적인 기능을 재사용하게 해주며 코드 중복(DRY)도 막아주기 때문에 굉장히 쓸모 있는 패턴입니다.

클래스 기반 뷰로 API 재작성하기

먼저 최상단 뷰를 클래스 기반 뷰로 재작성해봅시다. 이를 위해 views.py를 약간 리팩터링해야 합니다.

from snippets.models import Snippet  
from snippets.serializers import SnippetSerializer  
from django.http import Http404  
from rest_framework.views import APIView  
from rest_framework.response import Response  
from rest_framework import status


class SnippetList(APIView):  
    """
    코드 조각을 모두 보여주거나 새 코드 조각을 만듭니다.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

좋습니다. 이전 코드와 거의 똑같이 보이지만 HTTP 메서드를 분리했기 때문에 좀더 좋습니다. 마찬가지로 views.py 파일을 열어, 코드 조각 하나를 담당하는 뷰도 수정해봅시다.

class SnippetDetail(APIView):  
    """
    코드 조각 조회, 업데이트, 삭제
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

훌륭합니다. 그렇지만 역시나 아직까진 함수 기반 뷰와 크게 다르지 않습니다.

이제 urls.py도 수정해야 합니다. 클래스 기반 뷰를 사용해야 하니까요.

from django.conf.urls import url  
from rest_framework.urlpatterns import format_suffix_patterns  
from snippets import views

urlpatterns = [  
    url(r'^snippets/$', views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)  

자, 끝났습니다. 개발 서버를 실행해 보면 이전과 동일하게 작동하는 모습을 볼 수 있을 겁니다.

믹스인 사용하기

클래스 기반 뷰를 사용하여 얻는 가장 큰 이점은 기능들을 손쉽게 조합할 수 있다는 점입니다.

지금까지 사용한 생성/조회/업데이트/삭제 등의 명령은 일반적으로 모델을 사용할 때의 뷰와 비슷합니다. 이러한 보편적인 기능을 REST 프레임워크에서는 믹스인 클래스로 구현해두었습니다.

이제 뷰에 믹스인 클래스를 추가하는 모습을 살펴봅시다. views.py 파일입니다.

from snippets.models import Snippet  
from snippets.serializers import SnippetSerializer  
from rest_framework import mixins  
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,  
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

여기서 무슨 일이 벌어지는지 꼼꼼히 따져봅시다. GenericAPIViewListModelMixin, CreateModelMixin을 사용하여 뷰를 만들었습니다.

기본 뷰(GenericAPIView)는 핵심 기능을 제공하며, 믹스인 클래스들은 .list().create() 기능을 제공합니다. 여기서는 이 기능들을 getpost 메서드에 적절히 연결하였습니다. 이정도면 간단하죠?

class SnippetDetail(mixins.RetrieveModelMixin,  
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

비슷하게, 여기서도 GenericAPIView는 핵심 기능을 제공하며, 나머지 믹스인들이 .retrieve(), .update(), .destroy() 기능을 제공합니다.

제네릭 클래스 기반 뷰 사용하기

믹스인 클래스를 사용하여 뷰의 코드를 꽤 많이 줄였지만, 더 줄일 수 있습니다. REST 프레임워크에서는 믹스인과 연결된 제네릭 뷰를 제공합니다. 이를 사용하면 views.py 파일이 굉장히 짧아집니다.

from snippets.models import Snippet  
from snippets.serializers import SnippetSerializer  
from rest_framework import generics


class SnippetList(generics.ListCreateAPIView):  
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):  
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

보세요, 정말 간결하죠? 별 노력 없이 아주 많은 기능을 구현했는데도 코드는 더 깔끔하고 훨씬 Django다워졌습니다.

튜토리얼 4부에서는 인증과 권한을 API에서 어떻게 다룰지를 살펴보겠습니다.

Show Comments