# api/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
# 회원가입
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "username", "password")
extra_kwargs = {"password": {"write_only": True}}
def create(self, validated_data):
user = User.objects.create_user(
validated_data["username"], None, validated_data["password"]
)
return user
# 접속 유지중인지 확인
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "username")
# 로그인
class LoginUserSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Unable to log in with provided credentials.")
다른 시리얼라이저는 다 ModelSerializer로 작성하여 간단하게 처리하였고,
로그인의 경우 연결되는 모델이 없기 때문에 그냥 Serializer로 작성하였습니다.
4. 뷰 작성
다음은 views.py를 작성하겠습니다.
# api/views.py
from rest_framework import viewsets, permissions, generics, status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.decorators import api_view
from knox.models import AuthToken
from .serializers import CreateUserSerializer, UserSerializer, LoginUserSerializer
# Create your views here.
@api_view(["GET"])
def HelloAPI(request):
return Response("hello world!")
class RegistrationAPI(generics.GenericAPIView):
serializer_class = CreateUserSerializer
def post(self, request, *args, **kwargs):
if len(request.data["username"]) < 6 or len(request.data["password"]) < 4:
body = {"message": "short field"}
return Response(body, status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response(
{
"user": UserSerializer(
user, context=self.get_serializer_context()
).data,
"token": AuthToken.objects.create(user),
}
)
class LoginAPI(generics.GenericAPIView):
serializer_class = LoginUserSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data
return Response(
{
"user": UserSerializer(
user, context=self.get_serializer_context()
).data,
"token": AuthToken.objects.create(user)[1],
}
)
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
이번 뷰는 generic 기반 클래스 뷰로 작성되었습니다.
우선 함수 기반 뷰에서 클래스 기반 뷰로 바뀌면서 수정된 점은
앞서 데코레이터로 미리 http 메소드를 정의해주었던 것을 클래스 안으로 넣었다는 것이 있습니다.
또한 제네릭 뷰의 경우에는
기본적인 기능을 모두 포함하는 뷰로, 자세한 설명은 추후에 첨부하겠습니다.
5. url 설정
마지막으로 url을 설정하면 완료됩니다.
# api/urls.py
from django.urls import path, include
from .views import HelloAPI, RegistrationAPI, LoginAPI, UserAPI
urlpatterns = [
path("hello/", HelloAPI),
path("auth/register/", RegistrationAPI.as_view()),
path("auth/login/", LoginAPI.as_view()),
path("auth/user/", UserAPI.as_view()),
]
```
```python
# mysite/urls.py
from django.urls import path, include
from django.contrib import admin
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("api.urls")),
path("api/auth", include("knox.urls")),
]
6. 여태까지 내용 확인해보기
백엔드 API 테스트를 위해 Insomnia 라는 툴을 다운 받아 사용할 수 있습니다.
여러가지 편한 기능이 있으니 사용하시면 좋고, 아니면 그냥 기본 제공 웹 페이지에서 진행해도 됩니다.
회원 가입이나 로그인의 경우 POST 요청으로 보내면 되며 다음과 같이 요청을 보내면 됩니다.
- 회원가입
- 로그인
7. Django 기본 유저 모델 확장하기
여기까지 진행한 회원 모델은 Django에서 기본적으로 제공하는 유저 모델입니다.
하지만 실제로 사용하고 싶은 유저 모델은 포인트나 주소 등 여러 부가적인 정보가 들어갈 수 있는 유저이어야 합니다.
# api/urls.py
from django.urls import path, include
from .views import HelloAPI, RegistrationAPI, LoginAPI, UserAPI, ProfileUpdateAPI
urlpatterns = [
path("hello/", HelloAPI),
path("auth/register/", RegistrationAPI.as_view()),
path("auth/login/", LoginAPI.as_view()),
path("auth/user/", UserAPI.as_view()),
path("auth/profile/<int:user_pk>/update/", ProfileUpdateAPI.as_view()),
]
(5) 번외 admin에 Profile 등록
Django의 어드민 페이지에서 Profile 모델의 데이터를 보고 싶다면 admin.py에 등록해야 합니다.
또한 Profile 모델은 User 모델에 종속되어 있는 상황으로 User 인스턴스에 Profile 인스턴스가 포함되어 있는 형태로 보기 위해 다음과 같이 코드를 작성하여 등록합니다.
# api/admin.py
from django.contrib import admin
# Register your models here.
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from .models import Profile
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = "profile"
class UserAdmin(BaseUserAdmin):
inlines = (ProfileInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
지난 시간에 해당 주소로 요청을 보내면 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 사용을 권장한다는 것을 지난 시간에 말씀드렸습니다.
2. 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')
이렇게 만들어 볼 수 있습니다.
이제 우리는 한 가지 깨달음을 얻을 수 있습니다.
시리얼라이저는 응답으로 보낼 데이터의 형태를 정해주는 하나의 틀과 같구나!
3. 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(즉, 모델 인스턴스))를 시리얼라이저에 넣어 변환시키고
그 데이터를 응답으로 보내주는 것이 시리얼라이저 - 뷰 연동 개념입니다.
4. 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
위에서 작성한 시리얼라이저보다 훨씬 길고 복잡해졌죠?
원래 시리얼라이저를 선언할 때는
1. 데이터 형태에 대한 필드(앞선 방법에서는 fields, 지금은 일일이 선언),
2. 그리고 그 데이터를 어떻게 처리할건지에 대한 메소드가 필요합니다.
이 메소드는create나 update와 같이 이미 선언되어있는 메소드를 재작성하는 것이며,
views.py에서 직접 create나 update를 해야 할 것을 시리얼라이저에서 대신 해주는 것이라고 보면 됩니다.
대략 그런 역할을 선언해주고 필드에 대한 상세적인 내용을 직접 정의해야 했다면
serializers.ModelSerializer는 이 내용들을 알아서 해준 형태의 시리얼라이저라고 보시면 됩니다.
따라서 웬만하면 저희는 간편한 ModelSerializer의 형태로 선언하겠습니다. ★★
5. 마치며
여태까지 Django Rest Framework에서 가장 중요한 개념인 시리얼라이저에 대해 배웠습니다.
최근 정말 인기 있는 node.js의 express, python의 flask, java의 spring boot까지.
정말 다양한 언어로 개발된 프레임워크들이 있고
모든 분야가 그렇듯 다 경험 해보고 본인이 쓰기 편한 것으로 개발하시면 됩니다.
저 또한 express, flask, 그리고 Django Rest Framework 이렇게 세 종류를 경험해보았고
이 중에서 Django Rest Framework를 주 활용 스택으로 선택한 이유는 다음과 같습니다.
(1) Django!
: Django의 가장 큰 장점은 "Django스럽다" 라고 말할 수 있습니다. 이게 무슨 뜻이냐 하면, 개발해야하는 여러 대표적인 기능들이 거의 모두 이미 개발되었으니 그냥 가져다 쓰면 된다는 것입니다. 처음엔 오히려 이것저것 복잡해서 낯설고 친해지는데 오래 걸릴 수 있지만, 나중에는 이미 구현된 이 기능 하나하나에 정말 큰 고마움을 느낄 것입니다.
(2) Django의 강력한 ORM
: Django를 사용하다보면 내가 데이터베이스를 쓰고 있던가 싶을 정도로 개발자가 쓰기 좋게 잘 추상화 되어있습니다. 상당히 python 스럽고 직관적인 방법으로 데이터에 접근, 필터링할 수 있으며, 다른 프레임워크로는 얻을 수 없는 경험이라고 생각됩니다.
3. 프로젝트 소개
본 프로젝트에서는 Django Rest Framework를 활용하여 게시판을 만들어보겠습니다.
게시판을 만들면서 당연히 CRUD를 구현하게 될 것이고, 이 과정을 시작하면서 최대한 다양한 방법을 제시하고 그 중에서 한 가지 방법으로 통일하여 쭉 작업할 예정입니다.
또한 항상 프로젝트의 시작이 되면서 은근 귀찮고 어려운 회원 가입 / 로그인 인증도 개발할 예정입니다.