본문 바로가기

Django Rest Framework

02. Django Rest Framework, Serializer, View 개념 익히기

Hello World 코드 설명

# (수정) 추후 설명하는 그림을 첨부할 예정입니다.

GET 127.0.0.1:8000/api/hello/

 

지난 시간에 해당 주소로 요청을 보내면 hello world를 print하는 api를 만들었습니다. 각 단계를 다시 따라가보며 세부적인 내용을 살펴보겠습니다.

첫번째 단계는 views.py를 작성하는 것입니다. 현재 사용하고 있는 view의 구조는 함수 기반 뷰, 이른바 FBV 입니다.

# api/views.py

from rest_framework.response import Response
from rest_framework.decorators import api_view

# Create your views here.
@api_view(['GET']) # 해당 함수 view에서 처리할 http 메소드
def HelloAPI(request):
    return Response("hello world!") # http response 형태로 return

views.py에서 다룬 내용은 그냥 Django에서 다룬 것과 크게 다를 것 없이, 요청을 받아 해당 요청에 대한 응답을 제공하는 기능을 합니다. 오늘 배우게 될 serializer와 함께 하면 어떻게 할 수 있는지 이따 알아보겠습니다.

그 다음은 api/urls.py 입니다.

# api/urls.py

from django.urls import path, include
from .views import HelloAPI

urlpatterns = [
    path("hello/", HelloAPI),
]

크게 특별할 것 없이 view에 있는 HelloAPI를 가져와 url로 할당시켰습니다. path 사용을 권장한다는 것을 지난 시간에 말씀드렸습니다.

Serializer?

Serializer는 Django Rest Framework에서 나온 새로운 요소입니다. 사전적 의미는 직렬화 하는 무언가 정도로 볼 수 있습니다. 직렬화라는 말이 와닿지 않는데, 이는 간단하게 파이썬 데이터를 JSON 타입의 데이터로 변환해준다 정도로 생각하시면 됩니다.

기본적으로 웹에서 통신을 할 때, 즉 데이터를 주고 받을 때는 어느 정도 정해진 포맷이 있습니다. 대표적인 타입이 JSON이나 XML인데, 대부분의 REST API에서는 JSON으로 주고 받기 때문에 우리는 그냥 JSON만 잘 알고 있으면 됩니다:)

정확한 의미의 직렬화는 Django 프로젝트에서 내가 만든 모델로부터 뽑은 queryset, 즉 모델 인스턴스를 JSON 타입으로 바꾸는 것입니다. 기존 일반적인 Django에서의 폼과 같은 개념이라고 비유하는 경우가 있는데, 저는 그냥 Django 모델을 JSON으로 변환하기 위한 모양 틀 정도로 이해하고 있고 그게 제일 깔끔한 것 같습니다!

간단한 코드로 예를 들어보겠습니다.(게시판 프로젝트 코드가 아닙니다)

# test/models.py
from django.db import models
# 설명만을 위한 모델로, 상당히 대충 작성 되었습니다:)
class Person(models.Model):
    id = models.IntegerField()
    name = models.CharField()
    phone = models.CharField()
    addr = models.CharField()
    email = models.CharField()
# test/serializers.py
from rest_framework import serializers
from .models import Person

# ModelSerializer 뒤에서 설명합니다.
class BasePersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id', 'name', 'phone', 'addr')

위에서 보시면 Person이라는 모델을 models.py에 만들고, BasePersonSerializer라는 시리얼라이저를 serializers.py에 만들었습니다.(앱 내에 파일을 생성하셔야 합니다)

이게 어떤 뜻이냐면, "내가 만든 Person이라는 모델에서 데이터를 뽑아서 응답으로 보낼텐데, 응답의 형태 중 하나인 Base 형태를 BasePersonSerializer라고 정의할게!" 라는 뜻입니다.

자세히 보시면 모델에는 email도 정의되어있는데 Base의 fields에는 email이 없습니다. 이는 개발자가 정의한 Base 형태에는 email이 없다는 뜻이죠. 그러면 사람의 이메일 정보를 요청할 때 쓸 수 있는 시리얼라이저는 어떻게 만들 수 있을까요?

# test/serializers.py
from rest_framework import serializers
from .models import Person

# ModelSerializer 뒤에서 설명합니다.
class EmailPersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id', 'email')

이렇게 만들어 볼 수 있습니다. 이제 우리는 한 가지 깨달음을 얻을 수 있습니다.

시리얼라이저는 응답으로 보낼 데이터의 형태를 정해주는 하나의 틀과 같구나!

Serializer - View 연결하기

여기까지 잘 이해되셨다면 이후 단계에서는 더 잘 이해될 것입니다. 이제는 그럼 views.py에 실제로 시리얼라이저가 어떻게 사용되는지 알려드리겠습니다.

# test/views.py
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .models import Person
from .serializers import BasePersonSerializer, EmailPersonSerializer

@api_view(['GET'])
def PersonAPI(request, id):
    now_person = Person.object.get(id=id)
    serializer = BasePersonSerializer(now_person)
    return Response(serializer.data)
# => id=1에 대해 리턴된 Response: {'id': 1, 'name': '태뽕', 'phone': '01012345678', 'addr': '주소주소'}

@api_view(['GET'])
def EmailAPI(request, id):
    now_person = Person.object.get(id=id)
    serializer = EmailPersonSerializer(now_person)
    return Response(serializer.data)
# => id=1에 대해 리턴된 Response: {'id': 1, 'email': 'email@email.com'}

어떠신가요 바로 이해가 되셨나요? 제가 시리얼라이저를 변환기, 모양 틀로 설명드린게 이런 맥락이었습니다.

각 view에서 무언가 데이터를 요청할 때, 지금 예시에서는 PersonAPI는 사람에 대한 데이터, EmailAPI에서는 이메일에 대한 데이터를 요청할 때 각각 원하는 형태로 응답해줘야 합니다. 하지만 모델은 하나니 필요한 데이터만 골라서 보내줘야겠죠? 이 역할을 해주는게 시리얼라이저라고 이해하시면 됩니다.

내가 보낼 데이터(now_person(즉, 모델 인스턴스))를 시리얼라이저에 넣어 변환시키고 그 데이터를 응답으로 보내주는 것이 시리얼라이저 - 뷰 연동 개념입니다.

ModelSerializer

앞서 진행한 코드에서 한가지 설명 안하고 그냥 넘어간 코드가 있습니다. 바로 시리얼라이저를 선언할 때 사용한 serializers.ModelSerializer입니다.

# test/serializers.py
# ModelSerializer 뒤에서 설명합니다.
class BasePersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id', 'name', 'phone', 'addr')

원래 가장 기본적인 형태의 시리얼라이저 선언 방법은 serializers.Serializer를 상속 받는 것입니다. 이 형태로 선언하면 다음과 같이 코드를 작성할 수 있습니다.

# test/serializers.py
class BasePersonSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()
    phone = serializers.CharField()
    addr = serializers.CharField()

    def create(self, validated_data):
        """
        검증한 데이터로 새 `Person` 인스턴스를 생성하여 리턴합니다.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        검증한 데이터로 기존 `Person` 인스턴스를 업데이트한 후 리턴합니다.
        """
        ...
        ...
        return instance

위에서 작성한 시리얼라이저보다 훨씬 길고 복잡해졌죠? 원래 시리얼라이저를 선언할 때는 데이터 형태에 대한 필드(앞선 방법에서는 fields, 지금은 일일이 선언), 그리고 그 데이터를 어떻게 처리할건지에 대한 메소드가 필요합니다. 이 메소드는 create나 update와 같이 이미 선언되어있는 메소드를 재작성하는 것이며, views.py에서 직접 create나 update를 해야 할 것을 시리얼라이저에서 대신 해주는 것이라고 보면 됩니다.

대략 그런 역할을 선언해주고 필드에 대한 상세적인 내용을 직접 정의해야 했다면 serializers.ModelSerializer는 이 내용들을 알아서 해준 형태의 시리얼라이저라고 보시면 됩니다. 따라서 웬만하면 저희는 간편한 ModelSerializer의 형태로 선언하겠습니다.

마치며

여태까지 Django Rest Framework에서 가장 중요한 개념인 시리얼라이저에 대해 배웠습니다. 코드 작성 없이 보면서 따라가느라 조금 지루하셨을텐데, 다음 포스트에서는 이렇게 열심히 공부한 개념을 실제 게시판 프로젝트에 적용하겠습니다. 가장 첫 단계인 회원 인증부터 시작합니다:)