Django REST Framework 뷰 구현 방식 탐구

RESTful API의 기본 개념

Django REST Framework (DRF)를 활용하여 RESTful API를 구축할 때, 뷰(View)는 클라이언트의 요청을 처리하고 적절한 응답을 반환하는 핵심적인 역할을 합니다. RESTful 아키텍처는 자원(Resource) 중심의 설계 철학을 따르며, 각 자원은 고유한 URI를 통해 식별되고 HTTP 메서드(GET, POST, PUT, DELETE 등)를 사용하여 해당 자원에 대한 작업을 수행합니다.

예를 들어, 도서 정보를 관리하는 API에서 책 컬렉션과 개별 책에 대한 작업은 다음과 같이 매핑될 수 있습니다.

  • GET /api/books/: 모든 도서 목록 조회
  • POST /api/books/: 새로운 도서 생성
  • GET /api/books/{id}/: 특정 도서 상세 정보 조회
  • PUT /api/books/{id}/: 특정 도서 전체 업데이트
  • PATCH /api/books/{id}/: 특정 도서 부분 업데이트
  • DELETE /api/books/{id}/: 특정 도서 삭제

Django REST Framework 설치 및 직렬화

DRF를 사용하기 위해서는 먼저 라이브러리를 설치해야 합니다.

pip install djangorestframework

DRF는 데이터베이스 모델 객체를 JSON 또는 XML과 같은 형식으로 변환하거나, 그 반대로 클라이언트로부터 받은 데이터를 모델 객체로 변환하는 직렬화(Serialization) 기능을 강력하게 지원합니다. 특히 rest_framework.serializers.ModelSerializer는 Django의 ModelForm과 유사하게 모델에 기반하여 직렬변환기를 쉽게 정의할 수 있도록 해줍니다.

다음은 예시 모델과 그에 대한 ModelSerializer 정의입니다.

models.py

from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=100)
    location = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=200)
    isbn = models.CharField(max_length=13, unique=True)
    published_date = models.DateField()
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name='books')
    authors = models.ManyToManyField(Author, related_name='authored_books')

    def __str__(self):
        return self.title

serializers.py

from rest_framework import serializers
from .models import Book, Publisher, Author

class BookSerializer(serializers.ModelSerializer):
    # 읽기 전용 필드로 출판사 이름과 저자 이름을 포함
    publisher_name = serializers.ReadOnlyField(source='publisher.name')
    author_names = serializers.StringRelatedField(many=True, source='authors')

    class Meta:
        model = Book
        fields = ['id', 'title', 'isbn', 'published_date', 'publisher', 'publisher_name', 'authors', 'author_names']

직렬변환기는 모델 인스턴스 하나를 처리할 때는 단일 객체를 전달하고, 여러 인스턴스를 처리할 때는 many=True 옵션과 함께 쿼리셋을 전달합니다. APIView를 상속받는 DRF 뷰에서 이러한 직렬변환기를 활용하게 됩니다.

DRF 뷰 구현의 다양한 방법

DRF는 개발자가 API 뷰를 구현할 수 있도록 여러 추상화 레벨의 클래스를 제공합니다. 프로젝트의 복잡성이나 요구사항에 따라 적절한 뷰 클래스를 선택할 수 있습니다.

1. `APIView` 클래스 활용

APIView는 Django의 View 클래스를 확장하여 DRF가 제공하는 다양한 기능을 사용할 수 있도록 합니다. 요청/응답 객체, 인증, 권한, 스로틀링, 렌더러 등을 활용할 수 있으며, HTTP 메서드(get, post, put, delete 등)에 따라 직접 로직을 구현합니다. 이는 가장 기본적인 방법으로, 세밀한 제어가 필요한 경우에 유용합니다.

urls.py (예시)

from django.urls import path
from . import views

urlpatterns = [
    path('books/', views.BookListCreateAPIView.as_view(), name='book-list'),
    path('books/<int:pk>/', views.BookRetrieveUpdateDestroyAPIView.as_view(), name='book-detail'),
]

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 BookSerializer

class BookListCreateAPIView(APIView):
    """
    모든 도서 목록을 조회하거나 새로운 도서를 생성합니다.
    """
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)

    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 BookRetrieveUpdateDestroyAPIView(APIView):
    """
    특정 도서의 상세 정보를 조회, 업데이트 또는 삭제합니다.
    """
    def get_object(self, pk):
        try:
            return Book.objects.get(pk=pk)
        except Book.DoesNotExist:
            return None

    def get(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book)
        return Response(serializer.data)

    def put(self, request, pk):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookSerializer(book, 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):
        book = self.get_object(pk)
        if book is None:
            return Response(status=status.HTTP_404_NOT_FOUND)
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

2. `GenericAPIView`와 Mixins 조합

GenericAPIViewAPIView를 상속받아 querysetserializer_class와 같은 공통 속성과 메서드를 추가합니다. 여기에 CRUD(Create, Retrieve, Update, Delete) 작업을 위한 mixins 클래스들을 조합하여 코드의 재사용성을 높일 수 있습니다.

  • mixins.ListModelMixin: 여러 객체를 나열 (GET 요청)
  • mixins.CreateModelMixin: 새 객체 생성 (POST 요청)
  • mixins.RetrieveModelMixin: 단일 객체 조회 (GET 요청)
  • mixins.UpdateModelMixin: 단일 객체 업데이트 (PUT/PATCH 요청)
  • mixins.DestroyModelMixin: 단일 객체 삭제 (DELETE 요청)

serializers.py (추가 예시)

# ... (BookSerializer 등 기존 내용) ...

class PublisherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publisher
        fields = '__all__'

urls.py (예시)

from django.urls import path
from . import views

urlpatterns = [
    # ... (기존 Book URL 패턴) ...
    path('publishers/', views.PublisherListCreateAPIView.as_view(), name='publisher-list'),
    path('publishers/<int:pk>/', views.PublisherRetrieveUpdateDestroyAPIView.as_view(), name='publisher-detail'),
]

views.py (예시)

from rest_framework import generics, mixins
# ... (기존 APIView 클래스 및 임포트) ...
from .models import Publisher
from .serializers import PublisherSerializer

class PublisherListCreateAPIView(mixins.ListModelMixin,
                                 mixins.CreateModelMixin,
                                 generics.GenericAPIView):
    """
    모든 출판사 목록을 조회하거나 새로운 출판사를 생성합니다.
    """
    queryset = Publisher.objects.all()
    serializer_class = PublisherSerializer

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

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

class PublisherRetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                          mixins.UpdateModelMixin,
                                          mixins.DestroyModelMixin,
                                          generics.GenericAPIView):
    """
    특정 출판사의 상세 정보를 조회, 업데이트 또는 삭제합니다.
    """
    queryset = Publisher.objects.all()
    serializer_class = PublisherSerializer

    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)

3. Generic Concrete View 클래스

DRF는 GenericAPIViewmixins를 자주 함께 사용하는 패턴을 미리 정의해 둔 "Generic Concrete View" 클래스들을 제공합니다. 이들은 코드를 더욱 간결하게 만들어 줍니다.

  • generics.ListCreateAPIView: 목록 조회 및 생성 (ListModelMixin + CreateModelMixin)
  • generics.RetrieveUpdateDestroyAPIView: 상세 조회, 업데이트, 삭제 (RetrieveModelMixin + UpdateModelMixin + DestroyModelMixin)

serializers.py (추가 예시)

# ... (BookSerializer, PublisherSerializer 등 기존 내용) ...

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'

urls.py (예시)

from django.urls import path
from . import views

urlpatterns = [
    # ... (기존 Book, Publisher URL 패턴) ...
    path('authors/', views.AuthorListCreateAPIView.as_view(), name='author-list'),
    path('authors/<int:pk>/', views.AuthorRetrieveUpdateDestroyAPIView.as_view(), name='author-detail'),
]

views.py (예시)

# ... (기존 APIView, GenericAPIView + Mixins 클래스 및 임포트) ...
from .models import Author
from .serializers import AuthorSerializer

class AuthorListCreateAPIView(generics.ListCreateAPIView):
    """
    모든 저자 목록을 조회하거나 새로운 저자를 생성합니다.
    """
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

class AuthorRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
    """
    특정 저자의 상세 정보를 조회, 업데이트 또는 삭제합니다.
    """
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

4. `ModelViewSet` 클래스 활용

ModelViewSet은 특정 모델에 대한 모든 CRUD 작업을 하나의 클래스에서 처리할 수 있도록 완벽하게 통합한 가장 강력한 뷰 클래스입니다. 이는 GenericAPIView와 모든 mixins (List, Create, Retrieve, Update, Destroy)를 상속받습니다.

ModelViewSet은 일반적으로 DRF의 라우터(Router)와 함께 사용되어 URL 패턴을 자동으로 생성하지만, 수동으로 .as_view() 메서드를 통해 HTTP 메서드와 뷰셋 액션을 매핑할 수도 있습니다.

urls.py (예시 - 수동 매핑)

from django.urls import path
from . import views

urlpatterns = [
    # ... (기존 Book, Publisher, Author URL 패턴) ...
    path('authors-viewset/', views.AuthorViewSet.as_view({'get': 'list', 'post': 'create'}), name='authors-viewset-list'),
    path('authors-viewset/<int:pk>/', views.AuthorViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    }), name='authors-viewset-detail'),
]

views.py (예시)

from rest_framework import viewsets
# ... (기존 임포트) ...

class AuthorViewSet(viewsets.ModelViewSet):
    """
    Author 모델에 대한 CRUD 작업을 제공하는 뷰셋입니다.
    """
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

라우터를 사용하면 위와 같은 수동 URL 매핑 과정 없이, 뷰셋을 라우터에 등록하는 것만으로 모든 URL 패턴이 자동으로 생성됩니다. 이는 대규모 API 프로젝트에서 URL 관리를 크게 단순화합니다.

# urls.py (라우터 사용 예시)
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'books', views.BookViewSet, basename='book')
router.register(r'publishers', views.PublisherViewSet, basename='publisher')
router.register(r'authors', views.AuthorViewSet, basename='author')

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

# views.py (ViewSet 정의 예시)
from rest_framework import viewsets
from .models import Book, Publisher, Author
from .serializers import BookSerializer, PublisherSerializer, AuthorSerializer

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

class PublisherViewSet(viewsets.ModelViewSet):
    queryset = Publisher.objects.all()
    serializer_class = PublisherSerializer

class AuthorViewSet(viewsets.ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer

태그: Django DRF Django REST Framework REST API APIView

6월 17일 02:31에 게시됨