Django REST framework로 게시판 CRUD (1) | board

Jihun Kim·2021년 11월 1일
1

프로젝트

목록 보기
2/3
post-thumbnail
  • 이 글은 Django의 RESTframework를 이용해 게시판 CRUD를 구현한 과정을 담았습니다.
  • 게시판에는 '사용자'가 포함되어야 하기 때문에 custom user도 함께 구현 하였습니다.
  • 코드를 확인하려면 👉 깃허브 주소

Django REST framework

DRF(Django Rest Framework)란 Django 안에서 RESTful API 서버를 쉽게 구축할 수 있도록 도와주는 오픈소스 라이브러리다.


Restful API란?

HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미한다.

REST는 “Representational State Transfer”의 약자이며, 이는 자원을 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미한다.

👉 즉, 자원(resource)의 표현(representation) 에 의한 상태 전달을 말한다.

참고한 블로그


REST framework

  • REST framework는 장고에서 'RESTful API' 구축을 돕는 오픈소스 라이브러리이다.

그동안은 장고의 MVT 아키텍쳐를 이용하는 것만 해봤는데, 최근의 개발 트렌드는 프론트엔드와 백엔드를 분리하여 RESTful API를 이용해 통신하는 것이다. 최근에는 flask를 이용해 RESTful API 구축해 리액트로 만든 프론트엔드와 통신해 보았다.

하지만 플라스크는 마이크로 프레임워크이며 개발자가 모든 환경을 세팅해야 한다는 번거로움이 있다. 이 때문에 지난 프로젝트를 하면서도 굳이 플라스크를 사용해야 할까 하는 고민이 많았다. 반면 장고는 플라스크 보다 10배는 무거운 프레임 워크이며 이미 필요한 것들이 REST framework처럼 거의 다 개발되어 있어 편의성이 높다.


들어가기 전에....

프로젝트 구성

크게 'boards'와 'users' 앱으로 구성하였으며 각 앱의 model을 아래와 같이 users : boards의 일대다 관계로 구성하였다.


Boards 앱

1. models

/boards/models.py

from django.db import models
from users.models import User


class Board(models.Model):
    # 글의 제목, 내용, 작성일, 마지막 수정일
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="boards")
    title = models.CharField("제목", max_length=50, null=False)
    content = models.TextField("내용", null=False)
    dt_created = models.DateTimeField("작성일", auto_now_add=True, null=False)
    dt_modified = models.DateTimeField("수정일", auto_now=True, null=False)

    def __str__(self):
        return self.title

related_name은 장고 ORM모델을 위한 것이며,
ORM모델은 쿼리문 없이 장고에서 데이터베이스와 소통하기 위한 것이다.

  • author 필드에서 related_name='boards'라 지정하였다.
  • 아래와 같이 Comment 테이블에서 user가 User를 foreign key로 사용할 경우 related_name을 'comment'라 지정하면 해당 User의 Comment를 불러오기 위해서는 "comment= user.comment.all()"을 사용하면 된다.
class Comment(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name= 'comment' )

👉 related_name은 Comment가 참조하고 있는 User 모델에 생기는 것이기 때문에 참조해준 객체 입장에서 related_name을 설정해야 한다.(만약 related_name='user'라 한다면 "comment = user.user.all()"이라는 이상한 관계가 되어 버린다.)

참조한 블로그


2. Serializer 작성하기

Serialization(직렬화)은 컴퓨터 과학의 데이터 스토리지 문맥에서 데이터 구조나 오브젝트 상태를 동일하거나 다른 컴퓨터 환경에 저장하고 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다. 오브젝트를 직렬화하는 과정은 오브젝트를 마샬링한다고도 한다.

간단히 말하면 명시된 fields의 데이터를 json 형식으로 전달하는 역할을 한다고 볼 수 있다.

  • 게시판 내역을 출력할 때 '작성자', '제목'을 전달해야 하는데 Board 테이블에서 author는 User를 foreign key로 갖는다.
  • author의 email 혹은 name을 함께 전달하기 위해서는 아래와 같이 'get_author_email'을 이용한다. 해당 함수는 obj를 파라미터로 받으며 이는 Board가 된다. 따라서 'obj.author.email'로 접근하면 User에 정의된 author의 email을 가져올 수 있다.
  • serializers의 ModelSerializer는 내가 원하는 모델 필드만을 json으로 변경할 수 있도록 만든다.

/boards/serializer.py

from .models import Board
from users.serializers import UserSerializer
from rest_framework import serializers


class BoardSerializer(serializers.ModelSerializer):
    class Meta:
        model = Board
        fields = [
            "id",
            "author_email",
            "title",
            "content",
            "dt_created",
            "dt_modified",
        ]

    author_email = serializers.SerializerMethodField("get_authors_email")

    def get_authors_email(self, obj):
        return obj.author.email


class BoardDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Board
        fields = [
            "id",
            "author_email",
            "title",
            "content",
            "dt_created",
            "dt_modified",
        ]

    author_email = serializers.SerializerMethodField("get_authors_email")

    def get_authors_email(self, obj):
        return obj.author.email

3. View 작성하기

View를 작성하기 전, 다양한 class형 view가 있다는 것을 알게 되었다. 대표적으로는 'APIView', 'generic', 'Viewset'이 있는데 왼쪽에서 오른쪽 순으로 편의성이 높아진다. 검색해 보니 generic으로 만들어진 것이 많아, 일단 다수가 사용하는 것을 선택하기로 했다.

/boards/views.py

from .models import Board
from .serializers import BoardSerializer, BoardDetailSerializer
from rest_framework import generics
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from knox.auth import TokenAuthentication
from .permissions import IsOwnerOrReadOnly


class BoardAPI(generics.ListCreateAPIView):
    queryset = Board.objects.all()
    serializer_class = BoardSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)


class BoardDetailAPI(generics.RetrieveUpdateDestroyAPIView):
    queryset = Board.objects.all()
    serializer_class = BoardDetailSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsOwnerOrReadOnly]

BoardAPI

GET, POST Method 두 가지의 요청이 가능하다.

  • BoardAPI의 경우 로그인 하지 않아도 게시물을 읽을 수는 있다. 하지만 새로운 게시물을 작성할 수는 없다.
    👉 이를 구현한 것이 'permission_classes'의 'IsAuthenticatedOrReadOnly'이다. 이름 그대로 로그인 하지 않으면 읽는 것만 가능하다. 이는 rest_framework의 permissions에 구현된 것을 가져와 사용했다.
    👉 또한 authentication의 경우 knox.auth의 TokenAuthentication을 이용했다. 이는 users에서 토큰 인증을 사용할 때 knox.auth를 사용했기 때문인데 자세한 내용은 'Users' 파트에서 설명할 것이다.
  • POST 메소드로 요청시 BoardAPI의 'perform_create' 함수를 거치게 된다. 그러면 새 게시물 작성시 author가 request.user가 된다.
    👉 이를 위해서는 당연히 작성 요청시 header의 authroization에 토큰 값이 들어가야 한다.

BoardDetailAPI

GET, PUT, DELETE Method 세 가지의 요청이 가능하다.

  • 세부 게시물도 역시 로그인 하지 않은 유저도 보는 것은 가능하지만 수정과 삭제는 로그인한 유저만 가능하다.
    👉 이를 구현한 것이 'permission_classes'의 IsOwnerOrReadOnly이며 이는 permission을 커스텀 하여 만들었다.
  • permissions.py는 permissions의 BasePermission을 상속 받아 has_object_permission 메소드를 오버라이딩 하였다.

boards/permissions.py

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # 읽기 권한 요청이 들어오면 허용
        if request.method in permissions.SAFE_METHODS:
            return True

        # 요청자(request.user)가 객체(Board)의 author와 동일한지 확인
        if obj.author == request.user:
            return True

4. urls 작성하기

myweb/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("boards.urls")),
    path("auth/", include("users.urls")),
]
  • 아직 users 앱에 대한 내용이 추가 되지 않았지만 users 앱의 url은 'auth/'가 될 것이다.

boards/urls.py

from django.urls import path, include
from . import views

app_name = "boards"

urlpatterns = [
    path("board/", views.BoardAPI.as_view(), name="board-list"),
    path("board/<int:pk>/", views.BoardDetailAPI.as_view(), name="board-detail"),
]
  • class형 view는 불러온 다음 'as_view()' 메소드를 꼭 추가해야 한다. 그래야 view로 인식한다.

Postman으로 확인하기

💡 이미 seed_data를 생성했기 때문에 아래와 같은 데이터가 나오는 것입니다. seed data를 생성하는 데 사용되는 django_seed는 Django REST framework로 게시판 CRUD (3) 글에 작성할 예정입니다. 또한, '게시물 작성하기'에서 토큰 authroization과 관련한 부분은 Django REST framework로 게시판 CRUD (2)에 작성할 예정입니다!


게시판 전체 불러오기


게시물 작성하기

  • 아래와 같이 authroization을 header에 추가하고, body에는 'title'과 'content'를 작성해 서버에 요청합니다.
  • 그러면 작성자(author_email), 제목, 내용, 작성일, 수정일이 나오게 됩니다.

특정 게시물 보기

  • 위에서 생성한 id 55번의 게시물입니다.

특정 게시물 수정하기

  • PUT 혹은 PATCH 메소드로 요청할 수 있습니다.

  • 토큰을 제공하지 않으면 아래와 같이 토큰이 제공되지 않았다는 메시지가 뜹니다.

  • 토큰을 header에 함께 수정할 내용을 body에 담아 요청하면 아래와 같이 수정된 결과를 볼 수 있습니다.

특정 게시물 삭제하기

  • DELETE 메소드로 요청할 수 있으며 요청시 HTTP status는 204로 응답 받게 됩니다.
  • 수정할 때와 마찬가지로 header에 authroization에 관련한 user token을 함께 보내야 합니다.
profile
쿄쿄

0개의 댓글