본문 바로가기

Backend study/Django

APIView와 ViewSet

Django로 REST API를 개발할 때 가장 중요한 개념 중 하나가 바로 뷰(View)다. Django REST Framework(DRF)는 API 개발을 위한 강력한 뷰 클래스들을 제공하는데, 그 중에서도 APIView와 ViewSet은 가장 핵심적인 요소다.

 

APIView란?

APIView는 Django REST Framework에서 제공하는 클래스형 뷰로, HTTP 메서드(GET, POST, PUT, DELETE 등)를 메서드로 나누어 처리할 수 있게 해주는 뷰 클래스다.

일반 Django의 View와 거의 동일한 개념이지만, REST API 응답에 최적화된 기능들(요청 파싱, JSON 응답, 권한 처리 등)을 자동으로 포함한다.

 

Django View vs APIView 비교

기능 일반 Django View DRF APIView

응답 방식 HttpResponse, render() Response 객체로 JSON 응답
요청 파싱 수동으로 request body 파싱 자동으로 JSON 요청 파싱 (request.data)
권한 처리 기본 권한 없음 인증, 권한, throttle 쉽게 연동 가능
HTTP 메서드 HTTP 메서드 이름 직접 확인해야 함 메서드별(get(), post())로 자동 분기 처리

 

APIView 기본 사용법

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class HelloAPIView(APIView):
    def get(self, request):
        return Response(
            {"message": "GET 요청 성공!"}, 
            status=status.HTTP_200_OK
        )
    
    def post(self, request):
        name = request.data.get("name")
        return Response(
            {"message": f"안녕하세요, {name}님!"}, 
            status=status.HTTP_201_CREATED
        )

코드 설명

  • APIView 상속: REST API 뷰 만들기 위한 기본 클래스
  • request.data: JSON 요청 자동 파싱 (body → dict)
  • Response: DRF에서 제공하는 응답 객체 (JsonResponse 대체)
  • status: HTTP 응답 코드 상수 (HTTP_200_OK, HTTP_400_BAD_REQUEST 등)

 

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 ArticleAPIView(APIView):
    def get(self, request, pk=None):
        if pk:
            # 특정 게시글 조회
            try:
                article = Article.objects.get(pk=pk)
                serializer = ArticleSerializer(article)
                return Response(serializer.data)
            except Article.DoesNotExist:
                return Response(
                    {"error": "게시글을 찾을 수 없습니다."}, 
                    status=status.HTTP_404_NOT_FOUND
                )
        else:
            # 전체 게시글 목록
            articles = Article.objects.all()
            serializer = ArticleSerializer(articles, many=True)
            return Response(serializer.data)
    
    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
        )
    
    def put(self, request, pk):
        try:
            article = Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        
        serializer = ArticleSerializer(article, 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):
        try:
            article = Article.objects.get(pk=pk)
            article.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except Article.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

 

APIView의 추가 기능들

1. 권한 설정

from rest_framework.permissions import IsAuthenticated

class PrivateAPIView(APIView):
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        return Response({"message": "인증된 사용자만 볼 수 있습니다."})

2. 인증 설정

from rest_framework.authentication import TokenAuthentication

class SecureAPIView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        user = request.user
        return Response({"message": f"{user.username}님 환영합니다!"})

3. Throttling (요청 제한)

from rest_framework.throttling import UserRateThrottle

class LimitedAPIView(APIView):
    throttle_classes = [UserRateThrottle]
    
    def get(self, request):
        return Response({"message": "요청 제한이 적용된 API입니다."})

 

ViewSet이란?

ViewSet은 CRUD(API 기본 작업)를 자동으로 처리해주는 DRF의 클래스형 뷰 묶음이다. APIView보다 코드를 더 적게 써도 GET, POST, PUT, DELETE를 모두 처리할 수 있다.

 

APIView vs ViewSet 비교

항목 APIView ViewSet

메서드 작성 get(), post() 등 직접 작성 대부분 자동 처리 가능
CRUD 처리 수동 구성 자동 구성 (ModelViewSet 사용 시)
URL 연결 as_view() 사용 router 사용 (자동 URL 생성)
커스터마이징 완전한 제어 가능 일부 제한적

 

ModelViewSet 기본 사용법

from rest_framework.viewsets import ModelViewSet
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

단 3줄의 코드로 전체 CRUD API가 완성된다!

  • ModelViewSet은 조회/생성/수정/삭제를 모두 자동 구현
  • querysetserializer_class만 지정하면 됨

 

Router를 사용한 URL 자동 생성

# urls.py
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)

urlpatterns = router.urls

router.register() 한 줄이면 다음 URL들이 자동 생성된다:

  • GET /articles/ - 목록 조회
  • POST /articles/ - 새 게시글 생성
  • GET /articles/{id}/ - 특정 게시글 조회
  • PUT /articles/{id}/ - 특정 게시글 수정
  • DELETE /articles/{id}/ - 특정 게시글 삭제

 

ViewSet의 다양한 종류

1. ModelViewSet

전체 CRUD 기능을 모두 제공하는 ViewSet

from rest_framework.viewsets import ModelViewSet

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

2. ReadOnlyModelViewSet

조회만 가능한 ViewSet (list, retrieve만 제공)

from rest_framework.viewsets import ReadOnlyModelViewSet

class ArticleReadOnlyViewSet(ReadOnlyModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

3. ViewSet

기본 ViewSet (액션을 직접 정의해야 함)

from rest_framework.viewsets import ViewSet
from rest_framework.response import Response

class CustomViewSet(ViewSet):
    def list(self, request):
        queryset = Article.objects.all()
        serializer = ArticleSerializer(queryset, many=True)
        return Response(serializer.data)
    
    def create(self, request):
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)

 

ViewSet 커스터마이징

1. 추가 액션 정의

from rest_framework.decorators import action
from rest_framework.response import Response

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=True, methods=['post'])
    def set_favorite(self, request, pk=None):
        article = self.get_object()
        article.is_favorite = True
        article.save()
        return Response({'status': 'favorite set'})
    
    @action(detail=False)
    def popular(self, request):
        popular_articles = Article.objects.filter(views__gte=100)
        serializer = self.get_serializer(popular_articles, many=True)
        return Response(serializer.data)

2. 쿼리셋 필터링

class ArticleViewSet(ModelViewSet):
    serializer_class = ArticleSerializer
    
    def get_queryset(self):
        user = self.request.user
        return Article.objects.filter(author=user)

3. 권한 설정

from rest_framework.permissions import IsAuthenticatedOrReadOnly

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]

 

언제 무엇을 사용해야 할까?

APIView를 사용해야 할 때

  • 복잡한 비즈니스 로직이 필요한 경우
  • 표준 CRUD가 아닌 특별한 동작이 필요한 경우
  • HTTP 메서드별로 완전히 다른 로직이 필요한 경우
  • 세밀한 제어가 필요한 경우

ViewSet을 사용해야 할 때

  • 표준 CRUD 작업이 주된 경우
  • 빠른 개발이 필요한 경우
  • URL 패턴을 자동으로 생성하고 싶은 경우
  • 일관된 API 구조가 필요한 경우

 

Django REST Framework의 APIView와 ViewSet은 각각의 장점이 있다. APIView는 세밀한 제어가 가능하고, ViewSet은 빠른 개발이 가능하다. 프로젝트의 요구사항에 따라 적절히 선택하여 사용하면 된다.

일반적으로는 기본 CRUD는 ViewSet으로 빠르게 구현하고, 특별한 로직이 필요한 부분은 APIView로 구현하는 혼합 방식을 많이 사용한다.

728x90