Django REST Framework(DRF)에서는 API를 구축하기 위해 다양한 방식의 뷰 클래스를 제공합니다. 그 중 대표적인 것이 APIView, Generic View(제네릭 뷰), 그리고 ViewSet입니다. 각 방식은 추상화 수준과 편의성이 다르며, 상황에 따라 알맞게 선택할 수 있습니다. 이번 포스트에서는 이 세 가지 뷰 클래스의 차이점과 사용 사례를 살펴보고, 코드 예시를 통해 언제 어떤 것을 사용하면 좋을지 설명합니다.
APIView – 기본 클래스 기반 뷰
APIView는 DRF에서 제공하는 가장 기본적인 클래스 기반 뷰(CBV)입니다. Django의 표준 View 클래스를 상속하여 만들어졌으며, REST API 개발에 필요한 다양한 기능(인증/권한, 요청 데이터 파싱, 예외 처리 등)을 기본 제공한다는 점이 특징입니다. APIView를 사용하면 Django의 일반 CBV와 유사하게 HTTP 메서드별로 함수를 정의할 수 있습니다. 한 클래스가 하나의 엔드포인트(URL)를 처리하며, GET, POST, PUT, DELETE 등에 대응하는 메서드 (.get(), .post() 등)를 직접 구현합니다.
APIView의 가장 큰 장점은 유연성과 명시성입니다. 개발자가 요청별 동작을 직접 코드로 작성하기 때문에 세밀한 제어가 가능하고, 로직이 명확히 드러납니다. 따라서 복잡한 로직이나 표준 CRUD 범위를 벗어나는 특별한 처리가 필요할 때 적합합니다. (DRF의 제네릭 뷰나 뷰셋으로 구현하기 어려운 커스텀 동작을 손쉽게 다룰 수 있습니다.) 반면, 모든 것을 수동으로 처리해야 하므로 반복적인 CRUD 코드를 직접 작성해야 한다는 단점이 있습니다.
예를 들어, Article 모델에 대한 간단한 목록 조회(READ) 및 생성(CREATE) API를 APIView로 구현해보겠습니다:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer
class ArticleList(APIView):
def get(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data) # 기본적으로 200 OK
def post(self, request):
serializer = ArticleSerializer(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)
위 코드에서 ArticleList APIView는 GET 요청에 대해 모든 Article 목록을 직렬화하여 반환하고, POST 요청에 대해 전달된 데이터로 새 Article을 생성합니다. 이렇듯 APIView를 사용하면 직관적으로 요청별 동작을 정의할 수 있지만, 목록 조회와 생성 기능을 위해 일일이 쿼리셋 조회, 시리얼라이저 호출, 유효성 검증 등을 직접 처리해야 합니다.
Generic Views – 반복 작업의 추상화
DRF의 **제네릭 뷰(Generic Views)**는 APIView를 더욱 편리하게 활용하기 위한 미리 만들어진 클래스들입니다. GenericAPIView라는 기반 클래스가 있으며, 여기에 믹스인(Mixin) 클래스를 조합하여 자주 사용하는 패턴의 동작을 구현한 것입니다. 예를 들어 ListAPIView, CreateAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView 등이 있는데, 이러한 클래스들은 일반적인 CRUD 작업에 필요한 로직을 내부적으로 구현하고 있어서, 개발자는 최소한의 코드만 작성하면 됩니다. 제네릭 뷰를 사용하면 queryset과 serializer_class 같은 속성만 지정해주면 되고, 나머지 동작(조회, 생성, 수정, 삭제 등)은 DRF가 제공하는 기본 구현을 따르게 됩니다.
제네릭 뷰는 모델 객체를 다루는 표준적인 API에 적합합니다. 예를 들어 한 모델에 대해 목록조회와 생성이 필요한 경우, APIView로 구현하면 앞서 본 것처럼 코드를 직접 써야 하지만, DRF의 ListCreateAPIView를 활용하면 훨씬 간결합니다. 동일한 Article API를 제네릭 뷰로 구현해보겠습니다:
from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializer
class ArticleListCreateAPIView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
코드가 놀랍도록 간단해졌습니다. ListCreateAPIView는 내부적으로 **GET (list)**과 POST (create) 요청 처리 로직을 갖추고 있기 때문에, 개발자는 쿼리셋과 사용할 직렬화기만 지정하면 됩니다. 이렇게 하면:
- GET 요청 시 자동으로 queryset의 모든 객체를 불러와 ArticleSerializer로 직렬화한 후 응답하고,
- POST 요청 시 request.data를 ArticleSerializer로 역직렬화하여 유효성 검증 및 저장을 수행합니다.
필요하다면 제네릭 뷰 클래스에서 기본 동작을 오버라이드할 수도 있습니다. 예를 들어, queryset을 동적으로 변경하고 싶다면 get_queryset() 메서드를 재정의하거나, 응답 형식을 바꾸고 싶다면 list()나 create() 메서드를 재정의하여 기본 동작 위에 커스텀 로직을 추가할 수 있습니다. 하지만 대부분의 단순한 CRUD API에서는 제네릭 뷰의 기본 동작으로 충분하므로, 코드를 대폭 줄이고 생산성을 높일 수 있다는 점이 큰 이점입니다.
ViewSet – 여러 행동을 하나의 클래스에서 처리
ViewSet은 DRF에서 제공하는 가장 높은 수준의 추상화된 뷰 유형으로, 관련된 여러 API 엔드포인트의 로직을 하나의 클래스에 묶을 수 있게 해줍니다. 다른 프레임워크에서 "리소스(Resources)"나 "컨트롤러(Controller)"에 해당하는 개념으로 볼 수 있습니다. ViewSet은 일반적인 뷰와는 달리 .get()나 .post() 같은 HTTP 메서드 핸들러를 직접 제공하지 않고, 그 대신 .list(), .create(), .retrieve(), .update(), .destroy() 등의 액션 메서드를 제공합니다. 하나의 ViewSet 클래스로 목록 조회, 상세 조회, 생성, 수정, 삭제 등의 여러 동작을 모두 처리할 수 있으며, 각각의 액션은 나중에 URL 라우팅 시 대응되는 HTTP 메서드로 매핑됩니다.
ViewSet을 활용하면 RESTful한 여러 엔드포인트를 한 클래스에서 관리할 수 있어서 코드 구조가 깔끔해집니다. 특히 Router와 함께 사용하면 URL 패턴을 일일이 지정하지 않아도 자동으로 생성해주므로 편리합니다. 대표적인 예로 ModelViewSet이 있는데, 이는 ViewSet의 일종으로 GenericAPIView와 믹스인들이 결합되어 CRUD에 필요한 모든 액션(list, retrieve, create, update, destroy)을 기본 제공하는 클래스입니다. ModelViewSet을 사용하면 아래와 같이 최소한의 코드로 하나의 리소스에 대한 전체 API를 구현할 수 있습니다:
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
ArticleViewSet은 Article 모델에 대한 조회, 생성, 수정, 삭제 API를 모두 지원하는 뷰셋이 됩니다. 이 뷰셋을 URL에 연결하기 위해서는 **라우터(router)**를 사용합니다. 예를 들어 DRF의 DefaultRouter를 이용하면:
# urls.py
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
urlpatterns = [
# 다른 URL 패턴들 ...
*router.urls, # 라우터가 자동 생성한 URL들 포함
]
이렇게 등록하면 articles/ 경로에 대해 다음과 같은 엔드포인트들이 자동으로 만들어집니다.
- GET /articles/ → ArticleViewSet.list 실행 (Article 목록 조회)
- POST /articles/ → ArticleViewSet.create 실행 (새 Article 생성)
- GET /articles/<id>/ → ArticleViewSet.retrieve 실행 (특정 Article 상세조회)
- PUT/PATCH /articles/<id>/ → ArticleViewSet.update/partial_update 실행 (특정 Article 수정)
- DELETE /articles/<id>/ → ArticleViewSet.destroy 실행 (특정 Article 삭제)
개발자는 개별 엔드포인트를 위해 여러 클래스와 URL을 만들 필요 없이, 하나의 ViewSet 클래스와 한 줄의 router 등록으로 CRUD API 구성이 끝납니다. 기본 동작을 커스타마이징하고 싶다면 ViewSet 내의 각 액션 메서드를 오버라이드하거나, @action 데코레이터를 사용해 커스텀 액션을 추가할 수도 있습니다.
언제 어떤 것을 선택해야 할까?
마지막으로, APIView vs Generic View vs ViewSet 선택에 대한 간단한 지침을 정리해보겠습니다:
- APIView: 최대한 낮은 수준에서 세부 로직을 직접 제어하고 싶을 때 사용합니다. 예: 데이터베이스 질의뿐만 아니라 외부 서비스 호출이나 복잡한 비즈니스 로직 등 맞춤 처리가 많은 API. 또는 DRF의 추상화 없이 직관적인 코드로 작업하고 싶을 때. 초보자에게도 동작이 명확히 보여 이해하기 쉬우나, 기본적인 일도 모두 구현해야 하므로 코드량이 많아질 수 있습니다.
- Generic Views: Django 모델을 활용한 표준 CRUD API를 빠르게 만들 때 적합합니다. 시리얼라이저와 queryset만 설정하면 되므로 보일러플레이트 코드를 크게 줄여 생산성을 높일 수 있습니다. 예: 단순한 게시판의 목록/작성 API, 사용자 프로필 조회/수정 API 등 일반적인 패턴. 기본 동작은 대부분 커버되지만, 너무 특수한 경우에는 기본 제네릭 뷰가 맞지 않을 수 있습니다.
- ViewSet: RESTful하게 설계된 리소스의 전체 CRUD를 한번에 구성하고 싶을 때 사용합니다. 특히 대규모 API나 여러 엔드포인트를 체계적으로 관리해야 할 때 유용하며, Router를 통해 URL 관리도 자동화됩니다. 예: 상품, 게시글, 사용자 등 명확한 자원(Resource)에 대해 목록/상세/생성/수정/삭제 모든 API를 제공하는 경우. 기본 제공되는 관용적 동작에 딱 맞아떨어지므로 최소한의 코드로 구현 가능하지만, 추가적인 동작이 필요한 경우 액션 추가가 필요하며, 너무 많은 커스터마이징이 요구되는 경우에는 오히려 단일 ViewSet이 비대해질 수 있습니다.
요약하면, DRF는 개발 편의성과 유연성 사이에서 다양한 선택지를 제공합니다. 작은 기능의 단일 엔드포인트나 커스텀 처리가 많을 때는 APIView로 시작하고, 반복적인 CRUD 작업에는 제네릭 뷰를 활용하며, 전체적인 리소스 단위로 일관된 API를 설계할 때는 ViewSet과 Router 조합을 사용하는 식으로 선택할 수 있습니다. 각각의 도구를 적재적소에 활용하면, 효율적이면서도 유지보수가 쉬운 Django REST Framework API를 설계할 수 있을 것입니다.
'백엔드' 카테고리의 다른 글
Django와 DRF에서 Custom Middleware 사용법 (0) | 2025.05.16 |
---|---|
Django에서 HttpResponse와 DRF Response 차이점 (0) | 2025.05.15 |
Python과 Django 웹 개발 시작하기: 초보자를 위한 튜토리얼 (0) | 2025.05.14 |
Flask vs FastAPI vs Django: 파이썬 웹 프레임워크 비교 (0) | 2025.05.13 |
Django(장고) 웹 프레임워크 알아보기: 기본 개념, 프로젝트 구조와 장단점 (0) | 2025.05.12 |