본문 바로가기
Web/Django

[DRF] 2. APIView / Mixins / Generic CBV

by 조엘 2021. 1. 13.

안녕하세요! 파피몬입니다! ✨

백엔드 API 서버가 어떻게 구축되는지 조금 알 필요가 있다고 생각이 들어서 Django Rest Framework(이하 DRF)를 공부하고 있습니다. 아직 개발자를 지망하는 학생입니다! 틀린 부분이 있으면 댓글로 알려주시면 정말 감사하겠습니다!!

 

 

*** 개요 ***

현재 DRF에서 views.py를 Class Based Views(이하 CBV)로 작성하는 방식을 배우고 있다. CBV가 기존에 사용하던 Function Based Views(이하 FBV)에 비해 갖는 장점은 무엇일까? 궁금해서 찾아보았다. (참고: wikidocs.net/9623)

 

우선 해당 문서에 따르면, CBV를 통해서 다음과 같은 점이 장점이다. 

 1. HTTP 메소드에 따른 처리 코드 작성 시, 기존 FBV에서 사용하던 if 분기 대신 메소드 명으로 깔끔한 개발이 가능

 2. 다중 상속 같은 객체지향 기법을 활용해 제너릭 뷰, 믹스인 클래스 등을 통해 코드의 재사용개발 생산성 증대

 

처음에는 잘 안 와 닿았던 표현들이지만, 오늘 살펴볼 APIView, Mixins, Generic CBV를 공부하면서 장점을 느껴볼 수 있었다. 

 

 

*** APIView ***

APIView를 상속하여 view를 개발하는 방법을 알아보자. View를 개발한다는 것은, view단에 요청을 보내고 이를 처리하는 로직을 공부하는 것이다. 따라서 views.py와 urls.py의 코드를 분석해보자. 모든 코드는 DRF 공식 문서의 튜토리얼에서 가져왔다. (출처: www.django-rest-framework.org/tutorial/3-class-based-views/)

 

먼저 어떻게 urls.py에서 요청이 views.py로 넘어가는지부터 분석해보자. FBV 기반의 개발보다 훨씬 간결해지는 것을 볼 수 있는데, 이는 바로 .as_view() 라는 친구의 마법 덕분이다. 

# urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

 

.as_view()가 담당하는 역할은 다음과 같다. 잘 정리해주신 자료가 있어 첨부한다. (출처: ruaa.me/django-view/)

  1. 해당 클래스의 인스턴스를 생성한다. 

  2. 생성된 인스턴스의 dispatch() 메소드를 호출한다. 

  3. dispatch() 메소드는 요청을 검사하여 어떤 HTTP method 인지 알아낸다. 

  4. 인스턴스 내에 해당 이름을 갖는 메소드로 요청을 중재한다. 

  5. 해당 메소드가 정의되어 있지 않다면, 예외를 발생시킨다. 

 

"오케이! 그러면 'snippets/' 라는 HTTP URI로 get 방식의 HTTP method가 요청이 된다면, views.py의 SnippetList 클래스의 get 메소드를 호출해주면 되겠구나!" 이제 views.py를 구경해보자.  

 

APIView를 상속하여 view를 설계하는 것의 특징은 개발자가 직접 해당 액션에 대해 Response와 status를 명시해줘야함에 있다. 기존의 FBV 방식의 view 설계와 유사한 느낌이 든다! 또한 위에서 언급했듯, 클래스 내의 메소드 명은 HTTP method의 이름에 따라지어 준다. 해당 SnippetList 클래스 같은 경우, get 방식과 post 방식을 지원하게 설계했다고 생각할 수 있다. 

# 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):
    """
    List all snippets, or create a new snippet.
    """
    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)

 

 

*** Mixins ***

위에서 살펴봤던 APIView의 단점은 무엇일까? 바로 method 동작 로직이 모든 클래스에서 비슷한 역할을 수행함에도 클래스 별로 각각 비슷한 코드를 작성해 주어야 한다는 점이다. 해결을 위해 미리 만들어 놓은 mixins를 상속함으로써 중복을 거둬낼 수 있다!

# 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)

 

GenericAPIView를 상속받아 queryset(전달받은 모델의 객체 목록)과 직렬화를 담당하는 클래스를 등록해준다. 이 두 데이터는 Mixin의 로직을 처리하는 과정에서 사용된다. 

rest_framework.mixins 에서 가져온 코드

Mixin을 상속받으면서, 위의 APIView에서 다루었던 get, post의 로직은 이미 구현이 내부적으로 되어있음을 알 수 있다. 이를 상속받아서 사용만 하면 된다. 덕분에 조금 더 코드가 간결해 졌다!

 

마지막으로 해당 역할을 수행하기 위해 어떤 Mixin을 상속받아서 사용해야 할지 정리하고 넘어가도록 하겠다. 

  1. 데이터 목록을 가져오기 - mixins.ListModelMixin의 list 사용 

  2. 데이터 목록에 추가하기 - mixins.CreateModelMixin의 create 사용

  3. 디테일 데이터 가져오기 - mixins.RetrieveModelMixin의 retrieve 사용

  4. 디테일 데이터 수정하기 - mixins.UpdateModelMixin의 update 사용

  5. 디테일 데이터 삭제하기 - mixins.DestroyModelMixin의 delete 사용

 

 

*** Generic CBV ***

개발자 선배님들이 아마 Mixin을 상속하면서 쓰시다가 이것 마저 중복이 많다고 판단해 Generic CBV를 도입하셨나보다. 위에서 살펴본 mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView들을 미리 상속해 놓은 generics.ListCreateAPIView가 등장하였다. 

# 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

 

Generic CBV로 구현된 코드를 보면 이번엔 쿼리셋과 직렬화 클래스만 등록했다. ListCreateAPIView에서 미리 로직을 위의 mixin에서 살펴본 바와 같이 구현을 해두었기 때문에 같은 역할을 수행하게 된다.

 

이제 데이터 목록을 조회/생성할 때에는 위와 같이 generics.ListCreateAPIView를, 디테일 데이터를 조회/수정/삭제할 때에는 generics.RetrieveUpdateDestroyAPIView를 import 해서 사용하면 뚝딱 구현할 수 있게 되었다. 

 

코드가 이제 진짜 간결해졌다!

 

반응형

'Web > Django' 카테고리의 다른 글

[DRF] 4. Pagination / Filtering & Search  (0) 2021.01.15
[DRF] 3. ViewSet / Router  (0) 2021.01.14
[DRF] 1. 개요 / REST / JSON / Serializer  (0) 2021.01.13
[Django] 자소설 닷컴 총 복습!  (0) 2020.08.29
[Django] Django 소개  (0) 2020.05.16

댓글