Django REST Framework API 뷰는 클라이언트가 요청을 보내고 응답을 받을 수 있는 인터페이스를 제공합니다. DRF에서는 함수 기반 API 뷰 (FBV), 클래스 기반 API 뷰 (CBV), ViewSet을 사용하여 API를 더욱 효율적으로 구축할 수 있습니다.


Function-Based API Views

함수 기반 뷰는 간단한 API 구현에 적합한 방식으로, HTTP 요청에 따라 처리할 로직을 함수로 정의합니다. DRF에서는 @api_view 데코레이터를 사용하여 함수 기반 뷰를 구현할 수 있습니다.

# views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookModelSerializer

@api_view(['GET', 'POST'])
def book_list(request):
    if request.method == 'GET':
        books = Book.objects.all()
        serializer = BookModelSerializer(books, many=True)
        return Response(serializer.data)
    
    elif request.method == 'POST':
        serializer = BookModelSerializer(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)

위 예시에서 book_listGET 요청으로 모든 책을 반환하고, POST 요청으로 새로운 책을 추가하는 함수 기반 뷰입니다. DRF의 @api_view 데코레이터를 사용하여 이 뷰가 처리할 HTTP 메서드를 지정할 수 있습니다.


Class-Based API Views

클래스 기반 API 뷰는 보다 구조적이고 확장 가능한 방식으로, 클래스 내에서 여러 HTTP 메서드를 처리하는 로직을 정의합니다. DRF에서는 APIView 클래스를 상속받아 메서드를 구현합니다.

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookModelSerializer

class BookListView(APIView):
    def get(self, request):
        books = Book.objects.all()
        serializer = BookModelSerializer(books, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = BookModelSerializer(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)

위의 예시에서 BookListViewGET 요청으로 모든 책을 반환하고, POST 요청으로 새로운 책을 추가하는 API 뷰입니다. 클래스 기반 뷰는 각 HTTP 메서드를 클래스 내 메서드로 구현할 수 있어 관리와 확장이 용이합니다.

Mixins

Mixins는 DRF에서 기본적인 CRUD 작업을 처리할 수 있는 메서드들을 제공하는 클래스입니다. Mixins를 사용하면 클래스 기반 뷰에 Create, List, Retrieve, Update, Destroy 등의 작업을 처리하는 메서드를 사용할 수 있습니다.

# views.py

from rest_framework import mixins, generics
from .models import Book
from .serializers import BookModelSerializer

class BookListCreateView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

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

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


class BookRetrieveUpdateDestroyView(mixins.RetrieveModelMixin, 
                                    mixins.UpdateModelMixin, 
                                    mixins.DestroyModelMixin, 
                                    generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

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

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

    def delete(self, request, *args, **kwargs): # DELETE
        return self.destroy(request, *args, **kwargs)
  • BookListCreateView: GET과 POST 요청을 처리
  • BookRetrieveUpdateDestroyView: GET, PUT, PATCH, DELETE 요청을 처리

Generics

GenericsMixins보다 좀 더 추상화된 클래스로, CRUD 작업을 훨씬 간편하게 처리할 수 있도록 도와줍니다. GenericAPIView를 상속받아 querysetserializer_class를 지정하는 데 사용됩니다.

# views.py

from rest_framework import generics
from .models import Book
from .serializers import BookModelSerializer

class BookListCreateView(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

class BookRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
  • ListCreateAPIView: GET과 POST 요청을 처리
  • RetrieveUpdateDestroyAPIView: GET, PUT, PATCH, DELETE 요청을 처리

ViewSet

ViewSet은 DRF에서 CRUD 기능을 쉽게 구현할 수 있도록 도와주는 클래스입니다. ViewSet을 사용하면 RESTful API의 기본적인 CRUD 작업을 한 번에 자동으로 처리할 수 있으며, 이를 Router와 함께 사용하여 URL 패턴을 자동으로 생성할 수 있습니다.

# views.py

from rest_framework import viewsets
from .models import Book
from .serializers import BookModelSerializer

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
# urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet

router = DefaultRouter()
router.register(r'books', BookViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

BookViewSet은 ModelViewSet을 상속받아, Book 모델에 대한 CRUD 작업을 자동으로 처리합니다. DefaultRouter는 BookViewSet을 books/ 경로에 자동으로 매핑해줍니다. 이 방식은 RESTful API의 구현을 매우 간편하게 만들어 줍니다.

Override

APIView ViewSet에서 제공하는 다양한 메서드를 오버라이드하여 클라이언트의 요청을 효율적으로 처리하고, 추가적인 비즈니스 로직을 적용할 수 있습니다.

# views.py

from rest_framework import viewsets
from .models import Book
from .serializers import BookModelSerializer

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

    def get(self, request, *args, **kwargs):
        """
        GET 요청을 처리하여 모든 책 데이터를 반환합니다.
        """
        books = Book.objects.all()
        serializer = BookModelSerializer(books, many=True)
        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        """
        POST 요청을 처리하여 새로운 책을 생성합니다.
        """
        serializer = BookModelSerializer(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)

     def perform_create(self, serializer):
        """
        새로운 책을 저장할 때, 추가적인 로직을 수행할 수 있습니다.
        예를 들어, 책을 저장하기 전에 작성자를 현재 로그인한 사용자로 설정하는 등
        """
        user = self.request.user # 현재 로그인한 사용자
        serializer.save(user=user) # 책 저장 시 user 필드에 현재 사용자 지정

FilterSet

Django Filter를 사용하면 데이터를 조건에 맞게 필터링할 수 있습니다. DRF에서는 FilterSet을 사용하여 필터링 조건을 정의합니다. FilterSet은 필터링을 위한 조건을 정의할 수 있는 DRF의 도구입니다. django_filters 라이브러리를 설치한 후 필터셋을 정의하고 사용합니다.

pip install django-filter
# views.py

import django_filters
from rest_framework import viewsets
from .models import Book
from .serializers import BookModelSerializer

class BookFilter(django_filters.FilterSet):
    title = django_filters.CharFilter(lookup_expr='icontains')
    author = django_filters.CharFilter(lookup_expr='icontains')
    published_date = django_filters.DateFilter()

    class Meta:
        model = Book
        fields = ['title', 'author', 'published_date']

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
    filterset_class = BookFilter

위 예시에서는 BookFilter를 사용하여 title, author, published_date를 필터링할 수 있도록 설정하였습니다. 이제 API 요청에 필터링 조건을 추가하면 해당 조건에 맞는 데이터만 반환됩니다.

  • 제목으로 필터링: GET /api/books/?title=django
  • 저자 이름으로 필터링: GET /api/books/?author=John
  • 출판 날짜로 필터링: GET /api/books/?published_date=2020-01-01

Pagination

DRF에서는 페이징을 설정하여 데이터가 많을 때 성능을 최적화할 수 있습니다. 기본적으로 DRF는 페이지네이션을 지원하며, settings.py에서 설정을 변경하거나, PageNumberPagination 클래스로 변경하여 사용할 수 있습니다.

# settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10, # 한 페이지에 10개 데이터만 반환
}
# views.py

from rest_framework.pagination import PageNumberPagination
from rest_framework import viewsets
from .models import Book
from .serializers import BookModelSerializer

class BookPagination(PageNumberPagination):
    page_size = 5 # 한 페이지에 5개 데이터만 반환

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
    pagination_class = BookPagination

Permission Management

Django REST Framework는 API에 대한 접근을 제어할 수 있도록 permission_classes 속성을 제공합니다. 이 시스템을 통해 특정 사용자나 그룹에만 API 접근을 허용하거나, 사용자가 인증되었는지 등을 검사할 수 있습니다.

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

class BookView(APIView):
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        return Response({'message': 'Hello, world!'})
  • IsAuthenticated: 사용자가 인증되었는지 확인합니다. 인증되지 않은 사용자는 접근할 수 없습니다.
  • IsAdminUser: 사용자가 관리자 권한을 가지고 있는지 확인합니다. 관리자만 접근할 수 있습니다.
  • IsAuthenticatedOrReadOnly: 인증된 사용자에게는 읽기/쓰기 권한을 제공하고, 인증되지 않은 사용자에게는 읽기 권한만 제공합니다.
  • AllowAny: 모든 사용자가 접근할 수 있습니다. 인증 여부에 관계없이 API에 접근할 수 있도록 허용합니다.

Customization

기본 제공되는 권한 클래스 외에도, 자체적인 권한 클래스를 만들어 더 복잡한 권한 로직을 구현할 수 있습니다. 커스텀 권한 클래스는 permissions.BasePermission을 상속하여 정의합니다.

# permissions.py

from rest_framework.permissions import BasePermission

class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj):
        # 객체의 'owner' 필드가 요청한 사용자와 일치하는지 확인
        return obj.owner == request.user
from .permissions import IsOwner
from .models import Book

class BookView(APIView):
    permission_classes = [IsOwner]

    def get(self, request, pk):
        obj = Book.objects.get(pk=pk)
        self.check_object_permissions(request, obj) # has_object_permission 호출
        return Response({'message': 'You have access to this object.'})

References