1. 시리얼라이저란?

앞으로 모든 데이터를 json이라는 형식으로 주고받을 것이다.
json은 딕셔너리 형태와 비슷하게 생겼다!!

  • json의 특징
    딕셔너리처럼 생겼지만 스트링 형태이다.
    대부분의 언어에서 사용 가능하다.

- Response 사용하기

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

@api_view(['GET'])
def index(request):
    return Response("확인")

이전에는 template을 생성하고 render를 이용하여 화면을 띄웠지만 django rest framework에서는 Response를 이용해 화면에 띄운다.

위의 코드에서 POST를 추가하면

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

# Create your views here.
@api_view(['GET', 'POST'])
def index(request):
    return Response("확인")


이렇게 화면에 뜨게 된다!!!
api_view는 restframework에서 api들을 간단하게 사용할 수 있게 하는 기능을 한다.

- serializer

  • 방법 1
from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article

# Create your views here.
@api_view(['GET'])
def index(request):
    articles = Article.objects.all()

    return Response(articles)

이 방법으로 하면 오류가 난다.
왜?? 데이터를 넘겨주는 형식이 json형태가 아니기 때문에!!

  • 방법 2
from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article

# Create your views here.
@api_view(['GET'])
def index(request):
    articles = Article.objects.all()
    article = articles[0]
    article_data ={
        'title':article.title,
        'content':article.content,
        'created_at':article.created_at,
        'updated_at':article.updated_at,
    }
    return Response(article_data)

json 형태로 데이터를 넘겨주려면 위의 코드처럼 작성해야 하는데 이렇게 하면 코드가 너무 길어지게 된다.

때문에 이 문제를 해결하기 위해 사용하는 것이 바로 serializer이다!!
데이터를 json형태로 바꿔주는 기능을 한다!!!

2. 데코레이터(decorator function)란?

데코레이터란 쉽게 말하면 함수를 꾸며주는 함수이다!
인자를 함수로 받고 받은 함수값의 위 아래 부분에 새롭게 실행하는 값(코드)을 추가해서 꾸며진 함수를 리턴시켜준다!

예시 1) 데코레이터가 어떤식으로 작동하는지?

def wrapper_function(func):
    def decorated_function():
        print("함수 이전에 실행")
        func()
        print("함수 이후에 실행")
    return decorated_function

def basic_function():
    print("실행하고자 하는 함수")


new_function = wrapper_function(basic_function)
new_function()

예시 1 결과)

예시 2) 실제 데코레이터를 사용하면?

def wrapper_function(func):
    def decorated_function():
        print("함수 이전에 실행")
        func()
        print("함수 이후에 실행")
    return decorated_function

@wrapper_function
def basic_function():
    print("실행하고자 하는 함수")

basic_function()

# new_function = wrapper_function(basic_function)
# new_function()

예시 2 결과)


데코레이터 함수를 실행하면 basic_function만 두어도 @wrapper_function 때문에 예시 1과 동일한 결과가 나온다.
위처럼 @를 사용하여 코드를 축약시킬 수 있다!!

3. 모델 시리얼라이저 활용해보기

- 데이터 1개

from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET'])
def articleAPI(request):
    articles = Article.objects.all()
    article = articles[0]
    serializer = ArticleSerializer(article)  # ArticleSerializer이 article을 json형태로 바꿔준다.
    return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.

- 여러개의 데이터

from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET'])
def articleAPI(request):
    articles = Article.objects.all()
    serializer = ArticleSerializer(articles)  # ArticleSerializer이 article을 json형태로 바꿔준다.
    return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.

여러개의 데이터를 보낼 때 위와같은 방식으로 데이터를 전송하면 오류가 난다.
왜?? 데이터가 쿼리셋 형태이기 때문에!!
이럴 때는 serializer에 many=True라는 값을 넣어주면 된다!

from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET'])
def articleAPI(request):
    articles = Article.objects.all()
    serializer = ArticleSerializer(articles, many=True)  
    return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.

이렇게 작성하면 serializer가 article이 여러개가 온다고 인식하고 list형태로 데이터를 바꿔준다.

- POST 해보기

from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET', 'POST'])
def articleAPI(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)  
        return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.
    elif request.method == 'POST':
        serializer = ArticleSerializer(data = request.data)
        if serializer.is_valid(): # 데이터가 유효한지 검사해야함(필수)
            serializer.save() # 저장 필수
            return Response(serializer.data) # 완성된 데이터를 화면으로 돌려보내준다.
        else:
            print(serializer.errors)
            return Response(serializer.errors) # 에러값을 화면으로 돌려보내준다.(개발중에는 괜찮지만 보안상에는 좋지 않다.)

post 보낼때는 in_valid로 데이터가 유효한지 검사한 후 전송할 수 있다.

- status 전송하기

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET', 'POST'])
def articleAPI(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)  
        return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.
    elif request.method == 'POST':
        serializer = ArticleSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  • 404 오류
  • 201 성공

- 상세페이지, 수정, 삭제

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.generics import get_object_or_404
from articles.models import Article
from articles.serializers import ArticleSerializer

# Create your views here.
@api_view(['GET', 'POST'])
def articleAPI(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)  
        return Response(serializer.data)  # sertializer만 적으면 오류가 나고(데이터값이 없어서) .data를 붙여줘야된다. data에 결과값이 저장된다.
    elif request.method == 'POST':
        serializer = ArticleSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PUT', 'DELETE'])
def articleDetailAPI(request, article_id):
    if request.method == 'GET':
        article = get_object_or_404(Article, id=article_id)  # 이상한 id로 검색했을 때 404페이지가 뜨도록!
        serializers = ArticleSerializer(article)
        return Response(serializers.data)
    elif request.method == 'PUT':
        article = get_object_or_404(Article, id=article_id)
        serializer = ArticleSerializer(article, data = request.data) # article이 기존 데이터 data = request.data가 나중에 들어온 데이터
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
    elif request.method == 'DELETE':
        article = get_object_or_404(Article, id=article_id)
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
  • 상세페이지
  • 수정(업데이트)
  • 삭제

4. 포스트맨으로 API 확인해보기

포스트맨으로 게시글 확인, 작성, 수정, 삭제 모두 가능하다!

  • 주소 바꾸기?(환경 설정)
    나중에 개발 후 배포를 하는 상황에서 기본 주소가 바뀔 것이다.
    배포를 할 때쯤에는 수많은 api들이 쌓여있을 것이기 때문에 이를 모두 한번에 고치려면 환경설정을 해주어야 한다!!

5. json이란?

서버와 서버 혹은 클라이언트와 서버 사이에 데이터를 주고받을 때 사용하는 데이터 형식

많은 언어들이 키벨류 형식으로 되어있는데 통신을 위해 이것들을 json으로 바꾸는 것을 시리얼라이제이션이라고 한다.
반대로 json을 각 언어의 자료구조로 만드는 것을 디시리얼라이제이션이라고 한다.

파싱은 디시리얼라이제이션을 포함하는 더 넓은 의미로 데이터를 보고 의미를 해석하는 과정이다.
parse는 데이터의 의미 해석을 뜻한다.

<제이슨의 장점>

  • 명함카드 1장에 다 적히는 스펙
  • 버전 넘버가 없어서 영원히 1개의 버전이라는 안전성
  • 매일같이 바뀌는 웹개발 세계에서 끝까지 믿고 사용할 수 있는 몇 안되는 스텍

6. swagger 적용해 보기

swagger란 프로젝트 분석을 해서 알아서 자동으로 api를 만들어주는 툴
swagger 관련 자료 : https://drf-yasg.readthedocs.io/en/stable/readme.html

1. swagger 설치

pip install drf-yasg

2. settings.py에 추가

INSTALLED_APPS = [
   ...
   'django.contrib.staticfiles',  # required for serving swagger ui's css/js files
   'drf_yasg',
   ...
]

3. urls.py에 추가

...
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

...

schema_view = get_schema_view(
   openapi.Info(
      title="Snippets API",
      default_version='v1',
      description="Test description",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="contact@snippets.local"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=[permissions.AllowAny],
)

urlpatterns = [
   re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
   re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
   re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
   ...
]

4. 창 띄우기

해당 내용들을 추가하고 http://127.0.0.1:8000/swagger/ 로 들어가면 각 api들이 나오는 창이 뜬다!!

7. 클래스형 view로 바꿔보기

클래스형 view 예시 자료 : https://www.django-rest-framework.org/tutorial/3-class-based-views/
클래스형으로 view를 바꾸면 더 강력한 기능들이 추가된다.
=> 형태가 클래스여서 이것저것들을 상속받을 수 있다.

- views.py

class ArticleList(APIView):
    def get(self, request, format=None):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)  
        return Response(serializer.data)
                        
    def post(self, request, format=None):
        serializer = ArticleSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ArticleDetail(APIView):
    def get(self, request, article_id, format=None):
        article = get_object_or_404(Article, id=article_id)  # 이상한 id로 검색했을 때 404페이지가 뜨도록!
        serializers = ArticleSerializer(article)
        return Response(serializers.data)

    def put(self, request, article_id, format=None):
        article = get_object_or_404(Article, id=article_id)
        serializer = ArticleSerializer(article, data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, article_id, format=None):
        article = get_object_or_404(Article, id=article_id)
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

- urls.py

from django.urls import path
from articles import views

urlpatterns = [
    path('', views.ArticleList.as_view(), name='index'), # 클래스형 view를 가져올 때는 views.클래스형 이름.as_view로 작성해야 함.
    path('<int:article_id>/', views.ArticleDetail.as_view(), name='article_view'),
]

클래스형 view를 가져올 때는 views.클래스형 이름.as_view로 작성해야 한다!!

- swagger로 게시글 작성하기

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework.generics import get_object_or_404
from articles.models import Article
from articles.serializers import ArticleSerializer
from drf_yasg.utils import swagger_auto_schema

class ArticleList(APIView):
    def get(self, request, format=None):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)  
        return Response(serializer.data)
    
    @swagger_auto_schema(request_body=ArticleSerializer)                    
    def post(self, request, format=None):
        serializer = ArticleSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

위 코드처럼 from drf_yasg.utils import swagger_auto_schema@swagger_auto_schema(request_body=ArticleSerializer) 를 추가하고 서버를 실행하면 swagger 창에서도 게시글을 작성할 수 있다. 또한 api정보를 이전보다 자세하게 띄워준다!!

8. 예제 프론트엔드 만들어보기

- 백엔드에 있는 데이터 프론트엔드로 불러오기

1. 프론트엔드 파일

fetch 관련 자료 : https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Using_Fetch

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>프론트엔드</title>
    <script src="index.js"></script>
</head>
<body>
    <h1>프론트엔드</h1>
    <div id="articles">

        
    </div>
</body>
</html>
  • index.js
console.log("자바스크립트!")



window.onload = async function loadArticles(){ 
    const response = await fetch('http://127.0.0.1:8000/articles/', {method:'GET'})

    response_json = await response.json()

    console.log(response_json)

    const articles = document.getElementById('articles')

    response_json.forEach(element => {
        const newArticle = document.createElement('div')
        newArticle.innerText = element.title
        articles.appendChild(newArticle)
    });
}

2. 백엔드 파일

CORS 설치!!

(1) CORS 설치

pip install django-cors-headers

(2) settings.py 에 추가

INSTALLED_APPS = [
   ...,
   "corsheaders",
   ...,
]
MIDDLEWARE = [
   ...,
   "corsheaders.middleware.CorsMiddleware",
   "django.middleware.common.CommonMiddleware",
   ...,
]
CORS_ALLOW_ALL_ORIGINS = True

같은 도메인에서 요청을 보낼때는 허용이 되어있는데 다른 도메인에서 요청을 보낼 때는 보안상의 문제로 별도의 허용이 필요하다. 이 때 CORS로 허용해주면 된다.
CORS 관련 자료 : https://github.com/adamchainz/django-cors-headers

profile
개발과 지식의 성장을 즐기는 개발자

0개의 댓글