Django/DRF

DRF 와 마이크로서비스 튜토리얼

UserDonghu 2023. 10. 18. 14:00

DRF : Django REST Framework

Django를 기반으로 REST API 서버를 만들기 위한 라이브러리

 

모놀리식 : 여태까지 해온 방식으로 템플릿 문법을 써서 사용자에게 html, css, js코드를 주는 방법. API 명세표가 따로 필요없음. Server에서 렌더링을 다 해줌

- 장점 : 규모가 있지 않은 서비스의 경우 빠른 개발 가능, 소규모 팀인 경우 선택하기 좋음

- 단점 : 규모가 커질 경우 BE, FE의 역할이 혼재된다.

 

마이크로서비스 : Django서버와 FE서버를 별도로 운영하는것. 앞으로 DRF를 사용해서 마이크로식으로 구현해 볼 것이다.

- 장점 : 서버엔지니어와 프론트엔드개발자가 API 명세서로 소통할 수 있음

- 단점 : 소규모 프로젝트에서는 의사소통 비용이 올라감.


실습

Django프로젝트를 생성하고 blog앱을 만들고 settings.py에 추가한다.

 

라이브러리 설치

pip install djangorestframework

pip install django-cors-headers

 

settings.py 수정

INSTALLED_APPS = [
    # ~ 생략
    # django lib app
    'rest_framework', # 추가
    'corsheaders', # 추가
    # custom app
    'blog',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', #최상단 추가
    # ~ 생략
]

CORS_ORIGIN_ALLOW_ALL=True # 추가
CORS_ALLOW_CREDENTIALS=True # 추가

 

tutorialdjango와 blog에 urls.py 각각 등록한 후, views.py 수정

blog / views.py

def postlist(request):
    posts = [
        {'title':'1', 'content':'111'},
        {'title':'2', 'content':'222'},
        {'title':'3', 'content':'333'},
    ]
    return JsonResponse(posts, safe=False) # dict이외의 것을 받을 경우, safe=False

127.0.0.1:8000/blog

이렇게 Django서버에서 데이터를 보내고, 다른 별개의 FE서버에서 이 데이터를 받아서 페이지를 구성하는게 마이크로서비스다.



FE서버에서 Json데이터를 받아오는 html

<!DOCTYPE html>
<html lang="ko">
<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>
</head>
<body>
    <div id="data"></div>
    <script>
        // fetch로 http://127.0.0.1:8000/blog/ 데이터 가져와서 출력
        fetch('http://127.0.0.1:8000/blog/')
        .then(response => response.json())
        .then(data => {
            console.log(data);
            document.getElementById('data').innerHTML = data;
        });
    </script>
</body>
</html>

 

DRF 사용해보기

blog / views.py

from django.shortcuts import render
from .models import Post
from django.http import JsonResponse
# rest_framework 추가 후 추가된 코드
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

# FBV 사용하는 방식
@api_view(['GET']) # GET 요청을 처리한다는 데코레이터. ['GET', 'POST']하면 둘 다 처리 가능
def postlist(request):
    posts = [
        {'title':'1', 'content':'111'},
        {'title':'2', 'content':'222'},
        {'title':'3', 'content':'333'},
    ]
    serializer = posts # 직렬화 하는 단계. Python 기본 객체는 자동 직렬화 가능
    return Response(serializer) # Response로 반환 되었을 때 데이터를 읽을 수도 있고, POST를 보낼 수도 있음
# CBV 사용하는 방식
class MyView(APIView):
    def get(self, request):
        posts = [
            {'title':'1', 'content':'111'},
            {'title':'2', 'content':'222'},
            {'title':'3', 'content':'333'},
        ]
        serializer = posts # 직렬화 하는 단계. Python 기본 객체는 자동 직렬화 가능
        return Response(serializer) # Response로 반환 되었을 때 데이터를 읽을 수도 있고, POST를 보낼 수도 있음

postlist = MyView.as_view()

 

Serializer 클래스를 이용해서 직렬화 하기

 

blog / models.py 추가

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    head_image = models.ImageField(
        upload_to='blog/images/%Y/%m/%d/', blank=True)
    file_upload = models.FileField(
        upload_to='blog/files/%Y/%m/%d/', blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateField(auto_now=True)

    def __str__(self):
        return self.title

 

blog / serializers.py 추가

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer): # Post라는 모델을 직렬화 하겠다
    class Meta:
        model = Post
        fields = '__all__'

 

blog / views.py 수정

from django.shortcuts import render
from .models import Post
from django.http import JsonResponse
# rest_framework 추가 후 추가된 코드
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 .serializers import PostSerializer


@api_view(['GET', 'POST'])
def postlist(request):
    if request.method == 'GET': # GET 요청으로 DB 읽을 때
        postlist = Post.objects.all()
        serializer = PostSerializer(postlist, many=True) # postlist 직렬화. 다수의 Queryset을 넘길 때는 many=True
        return Response(serializer.data)
    elif request.method == 'POST': # POST 요청으로 DB에 추가할 때
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # 둘다 아니면 에러

CBV를 이용해서 구현한 postlist와 postdetail

from django.shortcuts import render
from .models import Post
from django.http import JsonResponse, Http404
# rest_framework 추가 후 추가된 코드
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 .serializers import PostSerializer

class PostList(APIView):
    def get(self, request):
        postlist = Post.objects.all()
        serializer = PostSerializer(postlist, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
class PostDetail(APIView):
    def get(self, request, pk):
        post = Post.objects.get(pk=pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)
    
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            raise Http404
        
    def put(self, request, pk):
        post = self.get_object(pk)
        serializer = PostSerializer(post, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        
    def delete(self, request, pk, format=None):
        post = self.get_object(pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    
postlist = PostList.as_view()
postdetail = PostDetail.as_view()