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 조합
GenericAPIView는 APIView를 상속받아 queryset 및 serializer_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는 GenericAPIView와 mixins를 자주 함께 사용하는 패턴을 미리 정의해 둔 "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