10. Django Rest Framework (2) - View 만들기

bruce1115·2023년 12월 24일
0

EduMax

목록 보기
11/12

DRF 함수형 View 살펴보기

이제 Serializer에 대해 학습했으니 이를 통해 DRF로 view를 만들어서 API를 만들어야 한다. View를 만드는 데는 클래스 기반의 view를 사용하는 것과 함수 기반의 view를 사용하는 것 둘 중 하나를 선택해야 한다. 함수형 View는 구현이 쉽고, 코드 로직을 보기에도 깔끔하다는 이점이 있다. 함수형 view를 사용하는 예시는 아래와 같이 공식 문서에 그대로 나와 있었다.

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(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)
  • 코드에 있는 api_view decorator는 함수형 view에서 작동한다. 클래스형 view가 아닌 함수형 view를 사용할 것이기 때문에, 이를 그대로 사용하면 된다. 코드에서 눈치챌 수 있듯이 Method를 제한하는 역할을 한다.
  • 기존 django의 view와 마찬가지로 인자로 request를 받는다. 만약 Post 요청인 경우 serializer를 통해 request 객체에서 데이터를 가져오게 된다.
  • status를 import해서 Resopnse 객체에 실어서 보낼 수 있다. 응답으로는 기본적으로 Response를 사용하는 듯?

Serializer를 응용하면 함수형 view를 쉽게 작성할 수 있다. HTTP Method는 조건문을 통해 하나씩 처리해 주는 것이 일반적이다. PUT이나 DELETE와 같은 다른 method에 대해서도 똑같이 처리해 줄 수 있고, serializer에 이미 delete, save등의 method가 정의되어 있기 때문에 CRUD 작업도 쉽게 처리할 수 있다. 기존의 django view에서처럼 pk 등의 인자를 url에서 가져오도록 새로운 parameter를 추가하는 것 역시 가능하다.

DRF 클래스형 View 살펴보기

이제 class-based view를 살펴 볼 시간이다. 앞선 View를 class 기반으로 바꾸면 다음과 같다. 역시 공식 문서에서 가져온 코드이다.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class SnippetList(APIView):
    """
    List all snippets, or create a new snippet.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(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)

조건문 분기 처리로 Method를 구분한 것을 get, post라는 함수로 따로 빼서 각각의 동작을 정의해 주었다. APIView라는 기본 클래스를 상속하는 것을 꼭 기억해 주어야 한다.
Class-based view를 사용하는 가장 큰 이점은 mixin을 사용하여 정형화된 CRUD 동작을 좀 더 쉽게 구현할 수 있다는 것이다. 이를 사용하지 않는다면 모든 반복적인 CRUD 작업을 각 클래스마다 반복적으로 구현해야 하는데, mixin은 이러한 번거로운 작업을 없애 준다. 이를 사용하기 위해서는 GenericAPIView를 상속해야 한다. 아래 코드를 보자.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

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

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
  • queryset : 기본적으로 model에 연결된 queryset을 사용하던가, get_queryset method를 overriding해서 사용해야 한다. get_queryset을 사용할 땐 반드시 해당 method를 사용하여 queryset을 불러와야 한다.
  • serializer_class : 입출력에 사용되는 serializer를 넣으면 된다. get_serializer_class라는 method를 overriding하는 방법도 존재한다고 한다.
  • 위 코드에는 나와 있지 않지만, 공식 문서에는 lookup_field를 지정할 수 있다고 나와 있다. 지정하지 않는다면 기본값은 'pk'이며, lookup_url_kwarg를 설정하여 url에서 설정해야 할 object lookup keyword를 지정해 줄 수 있다. 설정하지 않는다면 이는 lookup_field의 값과 같다.

쓸 수 있는 Mixin에 대해서도 간략하게 정리한다.

  • ListModelMixin : .list(request, *args, **kwargs) method를 사용할 수 있다. list라는 method에서 알 수 있듯이, queryset이나 serializer_class 등을 이용하여 DB 내 데이터들을 list 형태로 Response에 담아 return해 준다.
  • CreateModelMixin : create(request, *args, **kwargs) method를 사용할 수 있다. 역시 주어진 method들을 활용하여 request body에 담긴 데이터를 DB 내에 저장하고, 성공적이라면 201 Created 를, 실패하면 400 Bad Request 를 반환한다.
  • RetrieveModelMixin : .retrieve(request, *args, **kwargs) method를 사용할 수 있다. 앞서 lookup_field에 대해 이야기했는데, 이를 이용하여 DB 내의 데이터 중 해당하는 instance를 뽑아서 Response에 담아 반환한다. 성공 시 200 OK 를, 실패 시 404 Not Found를 반환한다.
  • UpdateModelMixin : .update(request, *args, **kwargs) method를 지원한다. 단, 부분적으로 업데이트할 경우는 .partial_update(request, *args, **kwargs) 를 써야 하며, 이 때 PATCH request를 지원한다. 성공 시 200 OK를, 실패 시 400 Bad Request를 반환한다.
  • DestroyModelMixin : .destroy(request, *args, **kwargs) method를 지원한다. 성공 시 204 No Content를, 실패 시 404 Not Found를 반환한다.

Mixin과 GenericAPIView를 합쳐 놓은 것이 바로 Concrete View Class이다. 이를 활용하면 간단하게 View를 다음과 같이 쓸 수 있다.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

하나로 합쳐 놓으니 queryset롸 serializer만 정의해 주면 CRUD를 할 수 있는 View를 매우 빠르게 만들 수 있게 되었다. 여러 가지 View Class가 있으니 간단한 CRUD의 경우 그대로 가져다 쓰면 매우 유용할 것 같다.

공식 문서에 더 많은 내용이 있지만, 직접 endpoint를 작성하면서 나머지 기능들을 차차 알아보기로 하겠다.

다음 목표

본격적으로 코드를 작성하기 전, 자동화된 Test 과정을 거치는 것이 좋겠다고 생각하였다. 따라서 코드를 작성하기 전에 Test를 어떻게 할 수 있을 지에 대해 공부해 보고자 한다.

profile
백엔드 개발자 꿈나무

0개의 댓글