클라이언트 측에서 우리 API로 요청을 보낼 때에는 URL과 Method를 조합하여 요청을 보내는데, 이때 같은 URL 내의 여러 Method를 처리하는 방법으로 함수형과 클래스형이 구분되었다.

그렇다면 URL과 Method의 조합은 몇 가지 정도 있을까? 일반적인 구조로는 다음의 5개의 조합이 존재한다.

DRF로 API를 개발할 때 만들어야 하는 기능은 GET(list) POST(create) GET(retrieve) PUT(update) DELETE(destroy)의 5가지가 전부이다. 이들은 모델 API 만들기에서 공통적으로 쓰이는 기능이므로 개발자들은 이를 최대한 편하고 쉽게 만들고자 하였고, 그럼 지금부터 그 방법에 대해 알아보자.
class BooksAPI(APIView):
def get(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request):
serializer = BookSerializer(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)
class BookAPI(APIView):
def get(self, request, bid):
book = get_object_or_404(Book, bid=bid)
serializer = BookSerializer(book)
return Response(serializer.data, status=status.HTTP_200_OK)
앞서 작성했던 CBV를 보면 books, book과 같은 모델로부터 가져온 데이터나 BookSerializer와 같은 시리얼라이저가 여러 번 사용되고 있음을 확인할 수 있다. 이 부분의 중복을 제거하기 위해 DRF에서는 mixins라는 것을 미리 정의하고 있다.
mixins는 APIView에서 request의 method마다 시리얼라이저 코드를 작성하는 것을 줄이기 위해 클래스 레벨에서 시리얼라이저를 등록하고 있다. 각 method에서는 시리얼라이저 코드를 작성하는 대신 각 method별 mixin에 연결하여 사용하기만 하면 된다.
from rest_framework import generics # generics 불러오기
from rest_framework import mixins # mixins 불러오기
from .models import Book
from .serializers import BookSerializer
class BooksAPIMixins(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): # ListModelMixin, CreateModelMixin 상속 + GenericAPIView는 공통적으로 상속
queryset = Book.objects.all()
serializer = BookSerializer
def get(self, request, *args, **kwargs): # GET - 전체 도서 조회
return self.list(request, *args, **kwargs) # ListModelMixin과 연결
def post(self, request, *args, **kwargs): # POST - 도서 1권 등록
return self.create(request, *args, **kwargs) # CreateModelMixin과 연결
class BookAPIMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): # RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin 상속 + GenericAPIView는 공통적으로 상속
queryset = Book.objects.all()
serializer = BookSerializer()
lookup_field = 'bid' # Django 기본 모델 pk가 아닌 bid를 pk로 사용하고 있으니 lookup_field로 설정한다.
def get(self, request, *args, **kwargs): # 도서 1권 조회
return self.retrieve(request, *args, **kwargs) # RetrieveModelMixin과 연결
# 도서 1권 수정, 도서 1권 삭제 기능 추가!
def put(self, request, *args, **kwargs): # 도서 1권 수정
return self.update(request, *args, **kwargs) # UpdateModelMixin과 연결
def delete(self, request, *args, **kwargs): # 도서 1권 삭제
return self.destroy(request, *args, **kwargs) # DestroyModelMixin과 연결
from django.urls import path, include
from .views import helloAPI, BooksAPIMixins, BookAPIMixins
urlpatterns = [
path("hello/", helloAPI),
path("mixin/books/", BooksAPIMixins.as_view()),
path("mixins/book/<int:bid>/", BookAPIMixins.as_view()),
]
CBV와 동일하게 .as_view()도 작성해야 한다.
mixins보다 간단하게 REST API의 5가지 메인 기능을 수행할 수 있다.
DRF generics는mixins를 조합해 일종의mixins 세트를 미리 만들어둔다.

from rest_framework import generics # generics 불러오기
from .models import Book
from .serializers import BookSerializer
class BooksAPIGenerics(generics.ListCreateAPIView): # 전체 도서 조회 + 도서 1권 등록
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookAPIGenerics(generics.RetrieveUpdateDestroyAPIView): # 도서 1권 조회 + 도서 1권 수정 + 도서 1권 삭제
queryset = Book.objects.all()
serializer_class = BookSerializer
lookup_field = 'bid'
from django.urls import path, include
from .views import helloAPI, BooksAPIMixins, BookAPIMixins
urlpatterns = [
path("hello/", helloAPI),
path("mixin/books/", BooksAPIMixins.as_view()),
path("mixins/book/<int:bid>/", BookAPIMixins.as_view()),
]
CBV와 동일하게 .as_view()도 작성해야 한다.
🥸 내가 이해한 대로-
APIMixins
- 정의할 때 아래에서 사용할 기능을 수행하는
mixin들을 선택적으로 상속받는다.generics.GenericAPIView는 공통적으로 상속받는다.get()post()함수 내용은 작성할 필요없이 그냥return self.기능()하면 된다.
APIGenerics
- 정의할 때 아래에서 사용할 기능을 수행하는
generic하나를 상속받는다.generics.GenericAPIView는 상속받지 않는다.get()post()함수 자체를 정의하지도 않고 따라서return구문도 작성하지 않는다. 여러 개의mixins를 사용하던 방식에서는 각mixins를 메소드에 매칭시키는 과정이 필요했고 그것이 return 구문에 있었다면, 이제mixins가 조합된generics를 사용하다 보니 어차피 List+Create면 GET과 POST가 들어간다는 것을 이미 알고 있는 것이 되기 때문이다.
여태까지 작업했던 것은 하나의 클래스가 하나의 URL을 담당하는 방식이었다. URL마다 클래스를 만들었고 각 클래스에서 해당 URL로 들어오는 다양한 메소드를 처리할 수 있도록 하였다.
그러다 보니 queryset이나 serializer_class 부분이 겹치게 되었다. 하나의 클래스로 하나의 모델을 전부 처리해 줄 수 있으면 겹치는 부분이 없어질 것이다. Viewset에서는 그러한 문제를 해결할 수 있다.
Viewset은 내부적으로 5가지 기능의mixins를 상속받아 정의된ModelViewSet을 이용해queryset과serializer_class를 정의해 주는 것으로 모델에 대한 기본적인 REST API를 만들 수 있다.
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
from rest_framework import routers
from .views import BookViewSet
router = routers.SimpleRouter() # 라우터 객체 선언
router.register('books', BookViewSet) # 라우터에 ViewSet 등록
urlpatterns = router.urls
urlpatterns에는 기존처럼 각 path를 일일이 등록하는 것이 아니라, router.urls를 include하여 마치 프로젝트의 urls.py에서 앱의 url을 불러오는 것처럼 작성하였다.