시작
DRF(Django Rest Framework) 튜토리얼을 따라 Serialization에 대해 알아보려고 합니다. 본 포스트에서는 macOS와 VS Code를 사용하여 진행합니다.
본문
프로젝트 생성 및 설정
먼저 프로젝트를 생성하고 필요한 설정을 진행합니다. 다음 명령어를 차례대로 실행하여 Django와 Django Rest Framework(DRF)를 설치하고 프로젝트를 생성합니다.
# 'drf_tutorial'이라는 디렉터리를 생성합니다.
mkdir drf_tutorial
# 'drf_tutorial' 디렉터리로 이동합니다.
cd drf_tutorial/
# 가상 환경(venv)을 생성합니다.
python3 -m venv venv
# 가상 환경을 활성화합니다.
source venv/bin/activate
# Django 패키지를 설치합니다.
pip install django
# Django Rest Framework 패키지를 설치합니다.
pip install djangorestframework
# Pygments 패키지를 설치합니다.
# (Pygments는 코드 하이라이팅을 위한 라이브러리입니다.)
pip install pygments
# Django 프로젝트를 'config'라는 이름으로 시작합니다.
# (현재 디렉터리에 생성되도록 '.'을 추가)
django-admin startproject config .
# Visual Studio Code로 프로젝트를 엽니다.
code .
# 'snippets'라는 이름의 새로운 Django 앱을 생성합니다.
python manage.py startapp snippetes
settings.py
파일 수정
DRF를 사용하기 위해 settings.py
파일에서 필요한 앱들을 추가해야 합니다. INSTALLED_APPS
에 rest_framework와 새로 생성할 snippets 앱을 추가합니다.
INSTALLED_APPS = [
...
'rest_framework', # DRF 추가
'snippets.apps.SnippetsConfig', # snippets 앱 추가
]
모델 구현
먼저 snippets/models.py
파일에서 Snippet
모델을 정의합니다. 이 모델은 코드 스니펫을 저장하는 역할을 합니다
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
# Pygments에서 사용할 수 있는 lexers(언어 목록)를 필터링하여 선택 가능한 언어 리스트를 만듭니다.
LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 사용할 수 있는 스타일 목록을 가져와서 정렬한 후 선택 가능한 스타일 리스트를 만듭니다.
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
# 선택 가능한 언어 목록을 사용하여 언어를 저장합니다.
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
# 선택 가능한 스타일 목록을 사용하여 코드 스타일을 저장합니다.
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ['created']
Snippet
모델을 정의한 후 이를 데이터베이스에 반영하기 위해 마이그레이션을 수행합니다.
python manage.py makemigrations snippets
python manage.py migrate snippets
Serializer class 생성
이제 snippets
디렉터리 아래에 serializers.py
파일을 생성하여, Snippet
모델에 대한 Serializer 클래스를 만듭니다. Serializer
는 데이터를 JSON 형태로 변환해주며 또한 유효성 검사를 수행합니다.
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
def create(self, validated_data):
"""
유효성 검사를 통과한 데이터를 이용해 새로운 `Snippet` 인스턴스를 생성합니다.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
기존의 `Snippet` 인스턴스를 업데이트하고 저장한 후 반환합니다.s
"""
instance.title = validated_data.get('title', instance.title)
instance.code = validated_data.get('code', instance.code)
instance.linenos = validated_data.get('linenos', instance.linenos)
instance.language = validated_data.get('language', instance.language)
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance
Serializers 사용
이제 Serializer
를 활용해 데이터베이스와 JSON 간의 변환을 확인합니다. 먼저 Django Shell을 열고 필요한 데이터를 생성하고 직렬화(Serialization) 작업을 수행해 보겠습니다.
1. Django Shell 실행
python manage.py shell
2. Snippet
데이터 생성 및 저장
Django Shell에서 아래 명령어를 하나씩 입력하여 Snippet
객체를 생성하고 저장합니다.
- Snippet 모델을 불러옵니다.
from snippets.models import Snippet
- SnippetSerializer를 불러옵니다.
from snippets.serializers import SnippetSerializer
- JSON으로 직렬화할 때 사용할 렌더러를 불러옵니다.
from rest_framework.renderers import JSONRenderer
- JSON 데이터를 파싱할 때 사용할 파서를 불러옵니다.
from rest_framework.parsers import JSONParser
- 새로운 스니펫을 생성하고 저장합니다.
snippet = Snippet(code='foo = "bar"\n') snippet.save()
- 또 다른 스니펫을 생성하고 저장합니다.
snippet = Snippet(code='print("hello, world")\n') snippet.save()
3. Serializer를 사용해 데이터 직렬화
SnippetSerializer
를 사용하여 데이터베이스에서 가져온 snippet
객체를 직렬화합니다. 직렬화된 데이터는 파이썬의 기본 데이터 형식으로 반환됩니다.
- 마지막에 저장된
Snippet
객체를 직렬화합니다. serializer = SnippetSerializer(snippet)
- 직렬화된 데이터를 출력합니다.
serializer.data # 결과: # {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
4. JSON으로 변환
serializer.data
로 얻은 직렬화된 데이터를 JSON으로 변환합니다.
- 직렬화된 데이터를 JSON 형식으로 변환합니다.
content = JSONRenderer().render(serializer.data)
- JSON 형식의 데이터를 출력합니다.
content # 결과: # b'{"id":2,"title":"","code":"print(\\"hello, world\\")\\n","linenos":false,"language":"python","style":"friendly"}'
5. JSON 데이터를 파싱하여 다시 파이썬 데이터로 변환
이제 JSON 데이터를 다시 파이썬의 기본 데이터 형식으로 변환합니다.
- 데이터를 스트림 형태로 다루기 위해 io 모듈을 사용합니다.
import io
- JSON 데이터를 바이너리 스트림으로 변환합니다.
stream = io.BytesIO(content)
- 스트림을 파싱하여 파이썬 데이터로 변환합니다.
data = JSONParser().parse(stream)
6. 파싱된 데이터를 이용해 새로운 Snippet
생성
파싱된 데이터를 바탕으로 새로운 Snippet
을 생성합니다.
- 파싱된 데이터를 이용해 Serializer를 생성합니다.
serializer = SnippetSerializer(data=data)
- 데이터가 유효한지 확인합니다.
serializer.is_valid() # 결과: # True
- 유효성 검사를 통과한 데이터를 확인합니다.
serializer.validated_data # 결과: # {'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}
- 새로운 스니펫을 저장합니다.
serializer.save() # 결과: # <Snippet: Snippet object (3)>
7. 여러 객체 직렬화
Snippet
모델의 모든 객체를 한꺼번에 직렬화하고, many=True
옵션을 사용하여 여러 개의 스니펫을 직렬합니다.
- 모든 Snippet 객체를 직렬화합니다.
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
- 직렬화된 데이터를 출력합니다.
serializer.data # 결과: # [{'id': 1, 'title': '', 'code': 'foo = "bar"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 3, 'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}]
리팩토링: ModelSerializer 사용
ModelSerializer
클래스를 활용하여 코드를 더 간결하게 리팩토링할 수 있습니다. 기존의 SnippetSerializer
를 ModelSerializer
를 사용하여 리팩토링해보겠습니다. 먼저 snippets/serializers.py
파일을 열어 다음과 같이 수정합니다:
from rest_framework import serializers
from snippets.models import Snippet
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ["id", "title", "code", "linenos", "language", "style"]
python Manage.py 셸을 사용하여 아래의 명령어를 입력하여 SnippetSerializer
의 동작을 확인합니다:
from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# 결과:
# SnippetSerializer():
# id = IntegerField(label='ID', read_only=True)
# title = CharField(allow_blank=True, max_length=100, required=False)
# code = CharField(style={'base_template': 'textarea.html'})
# linenos = BooleanField(required=False)
# language = ChoiceField(choices=[('abap', 'ABAP'), ... ]
ModelSerializer 클래스는 특별히 마법적인 작업을 수행하지 않으며 단순히 직렬 변환기 클래스를 생성하기 위한 shortcut
일 뿐입니다.
- 자동으로 결정된 필드 집합입니다
create()
및update()
메서드에 대한 간단한 기본 구현입니다.
Serializer를 사용하여 일반 Django 뷰 작성
이제 snippets/views.py
파일을 편집하여 일반 Django 뷰를 작성합니다.
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
snippet_list
뷰를 작성합니다.
@csrf_exempt
def snippet_list(request):
"""
모든 코드 snippet을 나열하거나 새로운 snippet을 생성합니다.
"""
if request.method == "GET":
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == "POST":
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
여기서는 @csrf_exempt
를 사용하여 CSRF 토큰 없이 클라이언트가 이 뷰에 POST 요청을 할 수 있도록 허용합니다.
snippet_detail
뷰를 작성합니다.
@csrf_exempt
def snippet_detail(request, pk):
"""
특정 코드 snippet을 조회, 수정 또는 삭제합니다.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return HttpResponse(status=404)
if request.method == "GET":
serializer = SnippetSerializer(snippet)
return JsonResponse(serializer.data)
elif request.method == "PUT":
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=400)
elif request.method == "DELETE":
snippet.delete()
return HttpResponse(status=204)
snippets/urls.py
파일을 생성하여 위에서 작성한 뷰를 URL에 연결합니다.
from django.urls import path
from snippets import views
urlpatterns = [
path("snippets/", views.snippet_list),
path("snippets/<int:pk>/", views.snippet_detail),
]
마지막으로 config/urls.py
파일에서 루트 URL 구성을 설정하여 snippets
앱의 URL을 포함시킵니다.
from django.urls import include, path
urlpatterns = [
path("", include("snippets.urls")),
]
웹 API에 대한 첫 번째 시도 테스트
앱을 실행합니다.
python manage.py runserver
브라우저에서 http://127.0.0.1:8000/snippets/
URL에 접속하면 다음과 같은 JSON 형식의 데이터가 표시됩니다.
[
{
"id": 1,
"title": "",
"code": "foo = \"bar\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
},
{
"id": 2,
"title": "",
"code": "print(\"hello, world\")\n",
"linenos": false,
"language": "python",
"style": "friendly"
},
{
"id": 3,
"title": "",
"code": "print(\"hello, world\")",
"linenos": false,
"language": "python",
"style": "friendly"
}
]
마무리
이로써 DRF의 Serialization을 살펴보았습니다. Serialization은 데이터의 직렬화와 역직렬화를 다루며 Django의 Forms API와 유사한 느낌을 줍니다.
참고자료
'Django' 카테고리의 다른 글
DRF Tutorial 3 Class-based Views (0) | 2024.09.14 |
---|---|
DRF Tutorial 2 Requests and Responses (0) | 2024.09.11 |
첫 번째 Django 앱 만들기 (Part 8: Adding third-party packages) (1) | 2024.09.02 |
첫 번째 Django 앱 만들기 (Part 7: Customizing the admin site) (0) | 2024.08.23 |
첫 번째 Django 앱 만들기 (Part 6: Static files) (0) | 2024.08.22 |
댓글