react인 프런트엔드에서 api통신을 위해 Django REST Framework라는 것을 사용해야 한다.
그렇다면 Django REST Framework는 뭘까?
Djagno REST Framework
Django 안에서 RESTful API서버를 쉽게 구축할 수 있도록 도와주는 오픈소스 라이브러리이다.
RESTful API란?
REST + API로 REST 아키텍처 규칙을 따라 만드는 API다. REST는 REpresentative State Transfer의 약자로 HTTP 프로토콜을 이용하는 url을 이용해서 HTTP Method를 통해 작업(CRUD)을 처리하는 방식이다.
REST API의 장점
1. simple / standardize
2. scaleable / stateless
3. high performance / caching
http표준을 따르는 여러 환경에서 어느 서버든 상관없이 api요청을 할 수 있는 게 매력적인 것 같다.
다음의 내용은 django REST framework 공식문서를 번역하여 정리했다.
Django REST Framework 사용하기
1. 환경 준비하기
a. 현재 작업 중인 프로젝트에 backend(다른 이름이어도 상관없음) 폴더 생성 및 이동
mkdir backend #backend폴더 생성
cd backend #backend폴더로 이동
b. 새 환경 만들기
먼저 우리가 이전에 작업했던 프로젝트(프런트엔드 부분 or 다른 프로젝트) 부분과 분리해서 패키지를 설치하기 위해 venv를 사용하여 가상환경을 생성해 준다.
#venv 설치 및 실행
python3 -m venv venv
source venv/bin/activate #이 부분은 window/linux/MacOS에서는 다름
#가상환경에 필요한 패키지 설치
pip install django
pip install djangorestframework
pip install pygments #이 패키지는 예시에 쓰이는 패키지로 설치 안해도 됨
가상환경에서 나가고 싶다면 deactivate 를 치면 나갈 수 있다.
c. 시작하기
먼저 프로젝트를 생성하고 앱을 만든다.
#프로젝트 생성
django-admin startproject tutorial
cd tutorial
#앱 생성
python manage.py startapp snippets
setting.py 의 INSTALED_APPS 에 rest_framework 와 앱이름 을 추가한다.
#/tutorial/settings.py
...
INSTALLED_APPS = [
...
'rest_framework',
'snippets', #앱이름
]
...
django REST Framework를 사용할 환경이 완성됐다.
2. 개발시작하기
본 튜토리얼에서는 예제로 코드조각을 저장하고 다루는 모델을 만드는 것이다.
a. 작업할 모델 만들기
models.py 로 이동하여 다음과 같은 모델을 추가해 준다.
#/snippets/models.py
from django.db import models
from pygments.lexers import get_all_lexers #lexer 가져오기
from pygments.styles import get_all_styles #코드 스타일 가져오기
LEXERS = [item for item in get_all_lexers() if item[1]] #lexer배열로 저장
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)
#auto_now_add : 오늘 날짜 자동으로 세팅
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']
model 변경 내용 저장하기 위해 migration을 해준다
python manage.py makemigrations snippets
python manage.py migrate snippets
마이그레이션이란?
models의 변경 내용을 db 스키마에 전파하는 django의 방법이다.
b. Serializer 클래스 만들기
api통신을 하기 위해서는 json, xml과 같은 형식으로 통신을 해야 데이터를 원활하게 받을 수 있다. 따라서 앱 api를 이용하기 위에 가장 먼저 해야 할 것은 snippet 인스턴스에 대해 json과 같은 표현형으로 serializing(직렬화)와 deserializeing(역직렬화)을 해줘야 한다.
여기서 serialize는 파이썬이 이해할 수 있는 데이터타입에서 json으로, deserialize는 json타입에서 파이썬이 이해할 수 있는 데이터타입으로 변경한다고 생각하면 된다.
그러기 위해서는 django의 forms와 매우 비슷하게 작동하는 serializers를 선언해줘야 한다.
먼저, snippets 디렉터리에 serializers.py 를 만들고 다음의 코드를 작성하자.
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')
#serialize/deserialize하는 부분
def create(self, validated_data):
"""
유효한 데이터가 주어지면 'Snippet' 인스턴스를 만들고 반환한다.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
유효한 데이터가 졌을때, 주어진 'Snippet' 인스턴스를 수정하고 반환한다.
"""
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
create() 와 update() 함수는 인스턴스를 만들거나 수정하도록 기능하고, serializer.save() 를 쓸 때 실행된다.
Serializer 클래스는 Django Form 클래스와 비슷해서 required , max_length , default 와 같은 비슷한 flag들을 가지고 있다.
c. serializers 다루기
Serailizer 클래스를 다루기 위해 Django shell 을 실행하자
python manage.py shell
일단 다루기 위해 필요한 것들을 import 해주고 code snippets을 추가해 주자.
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print("hello, world")\n')
snippet.save()
그리고 serializing 된 인스턴스를 확인해 보자.
serializer = SnippetSerializer(snippet)
serializer.data
#{'id': 2, 'title': '', 'code': 'print("hello, world")\n',
# 'linenos': False, 'language': 'python', 'style': 'friendly'}
이 경우에는 파이썬이 사용할 수 있도록 변환이 된 모델 인스턴스이다. 이 데이터를 serialization 해 json타입으로 렌더링을 해보자.
content = JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n",
# "linenos": false, "language": "python", "style": "friendly"}'
deserialization 하는 것도 비슷하다.
import io
#ByteIO는 내용을 다시 읽어들인다.
stream = io.BytesIO(content)
data = JSONParser().parse(stream)
다시 네이티브 데이터 타입으로 저장하자.
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
또한 모델 인스턴스들을 만들지 않고 바로 쿼리세트들을 serialize 할 수 있다.
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print("hello, world")\n')
snippet.save()
#이런식으로 안해도 됨
many=True 플래그를 쓰면 바로 쿼리세트들을 serialize할 수 있다.
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'),
# ('linenos', False), ('language', 'python'), ('style', 'friendly')]),
# OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'),
# ('linenos', False), ('language', 'python'), ('style', 'friendly')]),
# OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'),
# ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
d. ModelSerializers 사용하기
이전에 만들었던 SnippetSerializer 클래스는 많은 정보를 복사하고, Snippet 모델에 저장한다. 코드가 간결한 경우에는 상관없지만 코드가 복잡해질 경우에는 다른 방법을 사용해야 한다.
Django에 Form 클래스와 ModelForm 클래스가 있듯이, REST framework에는 Serializer 클래스와 ModelSerializer 클래스를 가지고 있다.
이전에 만들었던 serializer를 ModelSerializer 클래스로 고쳐보자
snippets/serializers.py 를 다시 열어서 다음과 같이 고치자.
#/snippets/serializer.py
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ['id', 'title', 'code', 'linenos', 'language', 'style']
serializers가 가지고 있는 장점 중 하나는 serializer 인스턴스의 모든 필드를 검사할 수 있다는 것이다.
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=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
ModelSerializer 클래스는 serializer 클래스를 만들 뿐 어떤 다른 작업도 하지 않는다. 필드가 자동으로 결졍되고 create() 와 update() 메서드가 기본적으로 구현된다.
e. Serializer를 이용하여 Django의 기본적인 view들을 작성하기
지금부터는 지금까지 만들었던 Serializer 클래스를 이용하여 API views를 만들 것이다.
먼저 views.py에 다음과 같이 작성하자.
#/snippets/views.py
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
@csrf_exempt
def snippet_list(request):
"""
코드 스니펫의 리스트를 보여주거나, 새로운 스니펫을 추가한다.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
#dictionary이외를 받을 경우, 두번째 argument를 safe=False로 설정해야한다.
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 토큰이 없는 클라이언트에서 이 뷰에 POST요청을 보낼 것이므로 @csrf_exempt 를 표기해 준다. 보통은 사용하지 않고, REST 프레임워크 뷰를 사용하는 데 있어서 권장하지 않는 방법이지만, 당장은 다른 방법을 쓰지 않아도 되기 때문에 추가해 줬다.
각각의 스니펫에 대해 스니펫을 반환하고, update 하고, delete 할 수 있어야 한다.
#/snippets/views.py
@csrf_exempt
def snippet_detail(request, pk):
"""
코드 조각을 반환하거나, update, delete 한다.
"""
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)
마지막으로 만든 view들을 url로 접속할 수 있어야 한다. url.py를 다음과 같이 만들자.
#/snippets/urls.py
from django.urls import path
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>/', views.snippet_detail),
]
이제 루트 url에서 이 snippets안에 있는 url로 접속할 수 있도록 연결을 해줘야 한다.
#/tutorial/urls.py
from django.urls import path, include
urlpatterns = [
path('', include('snippets.urls')),
]
지금까지의 예시에서는 제대로 처리할 수 없는 경우들이 존재한다. 만약 잘못된 형식의 json 을 보낸다거나, 우리가 만든 view들이 제대로 다루지 못하는 요청을 보내왔다면, 서버는 500 "server error" 답변을 보낼 것이다. 일단은 넘어가자.
3. 만든 web API 테스트하기
이제 몇몇 코드조각들을 가지고 샘플 서버를 시작해 볼 수 있다.
먼저 shell을 나가자.
quit()
그리고 django 개발 서버를 시작하자.
python manage.py runserver
Validating models...
0 errors found
Django version 4.0, using settings 'tutorial.settings'
Starting Development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
그다음 다른 터미널 창을 열고, 서버를 테스트해보자.
curl과 httpie를 가지고 지금까지 만든 API를 테스트해 볼 수 있다. Httpie는 파이썬으로 써진 user friendly 한 http 클라이언트이다. 먼저 설치를 해보자.
pip install httpie
그다음, 모든 snippet의 리스트를 불러올 수(get) 있다.
http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
...
[
{
"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를 참조하여 특정한 snippet을 얻을 수 있다.
http http://127.0.0.1:8000/snippets/2/
HTTP/1.1 200 OK
...
{
"id": 2,
"title": "",
"code": "print(\"hello, world\")\n",
"linenos": false,
"language": "python",
"style": "friendly"
}
브라우저에서 이 url을 접속하여 같은 json을 볼 수 있다.
참고자료
https://www.youtube.com/watch?v=lsMQRaeKNDk
https://blog.naver.com/sw_maestro/222930071929
https://www.django-rest-framework.org/tutorial/1-serialization/#creating-a-model-to-work-with
'웹 > Django' 카테고리의 다른 글
[REST framework] Serializers 1 (0) | 2024.04.14 |
---|