221014_DRF Basic (1)

Csw·2022년 10월 15일
0

Django

목록 보기
13/14
post-custom-banner

이 게시글은 김준태 강사님DRF - Basic 강의를 듣고 작성하였습니다.


📢 수업 목표

  1. DRF 로 간단한 블로그 만들기


✨ 간단한 Blog 만들기

🎨 poetry 로 django 프로젝트 만들기

Django Project 초기 셋팅 글을 참고하여 초기 셋팅 진행

🎨 rdb에 대해서

📌 블로그를 만들기 위해 필요한 것은?

  • 이용자가 글을 쓸 수 있고, 이용자가 쓴 글이 계속해서 쌓이면서 이를 다른 사람이 볼 수 있어야 함.

📌 바로 데이터베이스가 필요

  • 어떤 이용자 혹은 게시글이 있는지를 데이터베이스라는 정보 저장소를 통해서 확인하고, 사용자나 게시글을 해당 정보 저장소에 추가하는 형태로 작업이 이루어지게 됩니다.

📌 django 에서는 관계형 데이터베이스를 사용하는데, 이를 rdb (relational database) 라고 함.

  • 즉, 데이터베이스는 데이터베이스인데, 안의 데이터들이 서로 관계를 맺으면서 이루어져있다는 뜻!

📌 db 내부에서 특정 정보에 대해서 정보를 담는 공간을 테이블이라고 함.

  • 비유를 하자면, db 는 엑셀 파일이고, 테이블은 엑셀 파일 내부의 시트임.
  • 엑셀에서 시트를 여러개 만들 수 있듯, db 에서 테이블도 여러개를 만들 수 있음.

📌 유저가 글을 쓰는 기능을 만든다고 생각하면, 우선 user 테이블post 테이블이 각각 존재해야 함.

  • 각각 테이블에서 각각의 열 (id, title, body 등) 을 attribute 혹은 field 라고 말함.

📌 Post 테이블 내부의 userId 를 보고, User 테이블로 가서 해당 유저의 이름을 알 수 있음.

  • id 가 1인 글의 경우, userId 가 1 이므로, User 테이블에서 id 가 1인 사람을 찾으면,
    ‘김xx' 이라는 사람이 글을 썼다는 것을 알 수 있음.
    → 이렇게 테이블을 서로 연결짓는 필드를 foreign key 라고 함.
    (예시에서 post 테이블의 userId 필드를 foreign key 라고 말함.)
  • 그리고 이렇게 id로 특정한 유저를 구분짓기 위해서는, 해당 id 가 고유한 값 (unique) 이어야 함.
  • 다른 유저랑 id 가 똑같다면 다른 유저와 구별을 할 수가 없게 됨.
  • 그래서 이렇게 각 테이블마다 존재하는 unique 한 id 를 primary key 라고 함.

📌 보통은 아래와 같이 연결 구조를 표현하는 diagram 을 그려서 db 에 대한 초기 설계를
진행하게 됨.

🎨 model 설정

model : 글 저장 공간 만들기

📌 django 에서는 models.py 파일에 위와 같은 틀을 작성하고, 이후에 migration 이라는 행위를 통해서 실제 database 에 해당 테이블을 만들어주게 됨.

우선 틀을 만들기 위해, blog/models.py 에 모델 작성

blog.urls.py

from django.db import models

# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_time = models.DateTimeField(auto_now_add=True) # created_at 이라고도 합니다.
    updated_time = models.DateTimeField(auto_now=True)

📌 이렇게 하면 Article 이라는 이름으로 하나의 테이블을 만들 수 있게 됨.

  • 모델을 만들 때는 필드마다 타입 (글자인지, 시간인지 등) 을 지정해줘야 하는데, 사용할 수 있는 Field 로는 CharField (단어 / 짧은 텍스트), TextField (긴 글), DateTimeField (시간) 등이 있음.
    • auto_now_add : 처음 추가될 때 자동으로 현재 시각을 받아오겠다는 뜻
    • auto_now : 수정될 때마다 자동으로 현재 시각을 받아오겠다는 뜻
      → 이렇게 하면 사용자는 title, content 만 작성하면 되고, 작성 시각 등은 django 가 알아서 처리해줌.
    • id 를 굳이 직접 만들지 않은 이유는, primary key 로 쓸 필드는 굳이 직접 만들지 않아도 django 가 알아서 테이블 내에 id (혹은 pk) 라는 이름으로 필드를 만들어 주기 때문!

🎨 serializers 설정

serializers : 글에서 어떤 정보를 보여주거나 / 받을지를 지정

📌 requestresponse

  • request : www.naver.com 이라는 주소에 접근하는 것 자체가 하나의 요청.
  • response : 그리고 접근하는 순간에 거기에 대한 응답으로 이미지, 로그인을 위한 js 코드라던지, 데이터 들이 응답으로 오게 됨
  • 글을 작성할 때도, 하나의 요청으로 이러이러한 글을 써달라는 요청을 우리가 만든 서버 주소로 보내게 됨.
  • 거기에 대한 응답으로 글 작성에 성공했다 라는 내용을 내려줌.

📌 Django RestFramework 에는 serializer 라는 것이 있음.

  • 직렬화 (serialize) : db 에 저장되어있는 모델 객체를 프론트엔드로 전송 가능한 json 형태로 변형하는 것
  • 역질렬화 : 직렬화의 반대로 프론트엔드에서 넘어온 데이터를 db 에 저장할 수 있도록 변환하는 과정

    정확히는 db 객체 (instance) → dict → ByteString 으로 변환하는 과정을 serialize 라고 말함.

  • db 에 저장된 형태 그대로 웹사이트에 던져줄 수가 없으니, 이를 웹사이트에서 사용할 수 있는 형태로 가공하는 형태로 바꿔주는 과정임.

📌 drf 에서 serializer 를 작성할 때는 필요한 요소만 추출하도록 작성이 가능함.

  • 그래서 회원 정보 (id, password 가 포함된) 를 프론트엔드 (웹사이트) 로 넘기는 과정에서도,
    id 만 넘기도록 설정 가능.
  • 또한 serializer 를 통해서 현재 들어온 데이터가 유효한지 검사 (validation) 하는 과정 또한 진행하게 됨.
  • 그래서 우리가 글을 쓰기 위해서는 title, content 가 각각 필요하다고 serializer 에 명시를 했는데, 이에 대한 데이터가 안오거나 혹은 부정확하게 오는 경우에 serializer 는 해당 데이터를 db 에 저장하는 과정을 수행하지 않게 됨.

위 내용을 조금 더 단순화해서 설명하자면,

📌 serializer : 특정한 모델에 대해서, ‘프론트엔드와 통신할 때, 주고 받을 데이터의 목록 을 작성’ 하는 과정

  • 즉, 우선 글을 저장할 공간 (db) 에 글을 담기에 앞서서, 어떤 정보를 웹사이트 이용자로부터 받을지, 어떤 정보가 필요한지 등을 작성하는 과정.
    (또한 저장된 글을 이용자한테 보여줄 때 글의 어떤 정보를 보여줄 지 작성하는 과정이기도 함.)

더 단순하게 설명하자면, 프론트엔드와 통신할 때 주고 받을 데이터의 틀을 작성하는 과정!!

  • model을 생성함으로써 글을 작성할 공간을 만들어 놓았는데, 그래서 유저로부터 어떤 데이터를 받아서 작성할 것인지는 아직 안정함.
  • 유저로부터 어떤 데이터를 받고 어떤 정보를 유저한테 보여줄 것인지를 serializer.py에 작성

📌 serializer 는 기본적으로 아래와 같이 작성하는데, model 에 앞서 작성한 모델 (데이터를 가져오고 저장할 모델) 을 명시하고 fields 에는 웹사이트 이용자로부터 받을 + 이용자에게 표시해줄 데이터를 명시함.

blog/serializers.py

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = "__all__"   # ["title", "content"]와 같은 형태로 작성하면 해당 필드만 표시
  • created_time 등은 어차피 django 가 내용을 채우기 때문에, 그냥 fields 에는 사용자로부터 받을 title, content 만 작성해도 됨.
  • 그 경우 글 데이터를 프론트엔드에 전달할 때에도 title, content 만 넘겨주게 됨.

🎴 cf) 만약 프론트엔드로부터 받을 수도 있고, 안받을 수도 있는 데이터는 아래처럼 추가로 옵션을 작성해서 명시 가능

blog/models.py

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ["title", "content"]
        extra_kwargs = {"title": {"required": False, "allow_null": True}}
  • 지금 당장은 사용하지 않을 예정이지만, 위와 같은 방식으로 필요한 데이터 여부를 명시할 수 있음을 참고할 것
  • models.py 에 모델을 작성할 때에도 이를 명시할 수 있는데, models.py 와 serializer 에 아무런 명시도 하지 않을 경우 작성한 필드 전부 필수적으로 필요한 데이터로 인식함.

🎨 views 설정 (GET 요청)

views : 주소로 들어왔을때 실행될 내부의 작업 (db에 저장, db에서 가져오기) 을 지정

📌 views : 이용자 (프론트엔드) 로부터 요청 (+ 데이터) 을 받았을 때, 작동할 함수를 작성하는 곳

  • 실생활 비유

    • 실생활에 비유를 해보자면, 우리가 동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서 공무원에게 발급 서류를 제출함.
    • 그러면 공무원은 그 서류를 토대로 일정한 작업을 한 뒤에, 주민등록증을 발급해주게 됨.
  • 위의 예시를 웹사이트에 비유

    • "우리가 (웹사이트 이용자가) 동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서 (웹사이트 URL로) 공무원에게 발급 서류를 제출 (요청, Request) 함.
    • 그러면 공무원 (django) 은 그 서류 (요청에 포함된 데이터, Request Data) 를 토대로
      일정한 작업 (views.py 에 작성된 함수를 실행) 을 한 뒤에, 주민등록증을 발급해주게 됨. (응답, Response)"

📌 django 를 통해 글을 쓰는 과정 (POST 요청) 안내

  • 이 과정을 풀어서 설명하자면,
  • 웹사이트 이용자가 (글을 작성하려면), 글 작성 URL글 데이터를 포함해서 요청을 보냄
  • 그러면 django 는 해당 url 에 대응하는 views.py 내부의 함수를 실행
  • 이 views.py 내부의 함수는 글 데이터를 serializer 를 통해서 역직렬화 + 해당 글을 db 에 저장
  • 글 작성 성공 여부를 응답으로 전달해주게 됨.

📌 지금은 우선 글을 가져오는 것 (GET 요청) 부터 먼저 진행!

  • 이 과정을 풀어서 설명하자면,
    1. 웹사이트 이용자가 글 가져오기 URL 로 요청을 보내면,
    2. django 는 해당 url 에 대응하는 views.py 내부의 함수를 실행하고,
    3. 이 함수는 db 에 저장된 글들을 serializer 를 통해서 직렬화해서 글 데이터를 응답으로 전달
  • 그러면 지금 views.py 에
    1. db 에 저장된 글을 가져오고
    2. 글을 직렬화하고,
    3. 그렇게 직렬화한 글 데이터가 포함된 응답을 return 하는 함수를 작성

📌 CBV (Class Based View)

  • 특정 url 에 해당하는 views 함수를 하나의 클래스를 통해서 전부 작성하는 방식
    • BlogList 라는 클래스 안에 요청 Method 별로 함수를 생성
    • 똑같은 url 이라도 GET 방식의 요청이면 글을 db 에서 가져와서 보여주는 작업을 수행하고,
    • POST 방식의 요청이면 사용자가 보낸 데이터를 db 에 저장하는 작업을 수행하도록 작성 가능
      • 예시 : blog.com/posts/ 로 Get 요청을 보내면 작성된 글들을 받아올 수 있고, Post 요청을 보내면 글을 작성할 수 있음

blog/models.py

from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer

# Create your views here.
class BlogList(APIView):
	# permission_classes : (이 클래스와 연결된) Url 로 요청을 보내는 것에 대해 권한을 설정
    # allowany : 누구나 요청을 보낼 수 있도록 하는 것
    permission_classes = [AllowAny] 

    def get(self, request):
        article = Article.objects.all() # db 에서 글 가져오기
        serialized_article_data = ArticleSerializer(article, many=True).data # 가져온 글 직렬화하기
        return Response(serialized_article_data, status=status.HTTP_200_OK) # 직렬화한 데이터 응답으로 보내기
  • 우선은 get 요청부터 작성하는 것이므로, def get(self, request): 이라고 작성하고, 그 아래에 글을 가져와서 응답으로 전달하는 과정을 작성.
    • 이렇게 하면 자동으로 get 요청에 대응해서 지금 작성한 함수가 실행됨.

📌 django orm

  • db에서 글을 과져오는 과정으로는 다음과 같은 방식들이 있음.
    1. 전체 데이터 가져오기
      (models.py 에 작성한 모델명).objects.all()
    # Article.objects.all()
    # Article 테이블에서 모든 데이터를 다 가져옴.
    1. 일부 데이터 가져오기(데이터 여러개)
      (models.py 에 작성한 모델명).objects.filter(필드명="값")
    Article.objects.filter(title="안녕")
    # Article 테이블에서 제목에 '안녕' 이라고 되어있는 '데이터들'만 가져옴
    1. 특정 데이터 하나 가져오기 (데이터 딱 하나)
      (models.py 에 작성한 모델명).objects.get(필드명="값")
    Article.objects.get(pk=1)
    # Article 테이블에서 id 가 1 인 (첫번째로 작성된) 글을 가져옴

📌 데이터 직렬화

  • serializer 의 경우, 아래와 같은 형태로 사용해서 지금 가져온 데이터를 직렬화해줄 수 있음.

    (serializers.py 에 작성한 시리얼라이저이름)(데이터, many=True).data

    ArticleSerializer(article, many=True).data

📌 마지막으로는 직렬화한 데이터를 포함해서, 200 이라는 HTTP CODE 와 함께 response 형태로 응답함.

  • 이러면 프론트엔드에서는 해당 글 데이터를 받아볼 수 있게 됨.
    return Response(serialized_article_data, status=status.HTTP_200_OK)

🎨 urls 설정

urls : 어떤 주소로 들어와야 글이 작성되거나 / 글을 보여줄 지

  • urls.py 는 앞서 보았던 views.py 에 작성한 함수를 특정한 url 과 연결하는 과정을 수행하는 곳
  • www.naver.com 로 들어가면 네이버 화면이 보여지듯, (서버주소)/blog/ 로 들어가면 글을 볼 수 있게 작성할 예정.

  • urls.py 는 앱마다 하나씩 작성하고, 그렇게 작성된 것들을 config (프로젝트 폴더) 내부의 urls.py 에다가 몰아넣는 식으로 작성.

blog/urls.py

from django.urls import path
from blog.views import BlogList # 방금 작성한 views.py 내부의 클래스 import

urlpatterns = [
    path("", BlogList.as_view()),
]

config/urls.py

  • config/urls.py 는 아래처럼 작성해서, blog/urls.py 내부의 url 로 접근하려면 무조건 blog/ 를 붙여야하도록 작성.
    • 만약 위의 blog/urls.py 에 글을 보기 위한 url 을 asd/라고 작성해놓고, config/urls.py 에 path("blog/", include("blog.urls")) 라고 작성했다면, 글을 보려면 (우리 서버주소)/blog/asd/ 로 접속해야 함.
from django.urls import path
from blog.views import BlogList # 방금 작성한 views.py 내부의 클래스 import

urlpatterns = [
    path("", BlogList.as_view()),
]

🎨 admin 설정 후 서버 실행

admin : 관리자 페이지

  • 이를 이용하면 현재 models 에 등록된 테이블에 데이터를 쉽게 채울 수 있음.

blog/admin.py

from django.contrib import admin
from .models import Article

# Register your models here.
admin.site.register(Article) # 괄호 안에는 admin 페이지에서 관리할 모델명이 들어가야함

📌 관리자 계정 생성

Terminal에 입력

$ poetry run python manage.py createsuperuser

📌 서버 실행

Terminal에 입력

$ poetry run python manage.py runserver
  • 이제 127.0.0.1:8000/admin 로 들어가서, 방금 만든 관리자 계정으로 로그인
  • 아래 메뉴에 들어가서 글을 하나 추가
  • 이제 127.0.0.1:8000/blog/ 에 들어가면 아래처럼 표시되며 방금 작성한 글이 확인됨.
    • 127.0.0.1:8000/blog/ 로 GET 요청을 보내면 해당 응답이 온다는 뜻.
      • id 는 글이 작성된 순서대로 1번부터 부여됨.

🎨 게시글 쓰기 (POST 요청 작성)

  • 이제 게시글을 작성하기 위한 코드를 작성할텐데, 지금 게시글을 저장할 공간과 필요한 내용 (model/serializers) 는 이미 작성되어 있으니 views.py 만 추가로 작성
  • POST 요청과 관련해서 앞서 언급했던 내용을 한번 더 정리

    1. 웹사이트 이용자가 (글을 작성하려면),
    2. 글 작성 URL (현재는 글 작성 URL, 가져오기 URL 이 동일함)글 데이터를 포함해서 요청 전송
    3. 그러면 django 는 해당 url 에 대응하는 views.py 내부의 함수를 실행
    4. 이 views.py 내부의 함수는 글 데이터를 serializer 를 통해서 역직렬화 + 해당 글을 db 에 저장
    5. 글 작성 성공 여부를 응답으로 전달
  • 위의 과정도 아래와 같은 코드로 표현 가능

    def post(self, request):
        article_serializer = ArticleSerializer(data=request.data) # 시리얼라이저(data=request.data) 이라고 하면 request.data(사용자가 보낸 데이터)를 역직렬화하겠다는 뜻 / (data=) 이 없이 그냥 () 라면 직렬화
        if article_serializer.is_valid(): # 유효성 검사 (시리얼라이저가 자체적으로 가지고 있는 기능, 우리가 커스텀할 수 있음)
            article_serializer.save() # 유효하다면 db 에 저장 (시리얼라이저가 자체적으로 가지고 있는 기능, 저장하는 과정도 우리가 커스텀 가능 -> 다음 시간에 할 예정)
            return Response({"message": "정상"}, status=status.HTTP_200_OK)
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

blog/views.py를 아래처럼 수정

  • 위의 함수를 추가한 것
class BlogList(APIView):
    permission_classes = [AllowAny] 

    def get(self, request):
        article = Article.objects.all()
        serialized_article_data = ArticleSerializer(article, many=True).data
        return Response(serialized_article_data, status=status.HTTP_200_OK)

    def post(self, request):
        article_serializer = ArticleSerializer(data=request.data)
        if article_serializer.is_valid():
            article_serializer.save()
            return Response({"message": "정상"}, status=status.HTTP_200_OK)
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

🎨 postman 사용해보기

📌 postman : 우리가 만든 서버로 직접 get, post 요청 등을 보내볼 수 있도록 도와주는 프로그램

https://www.postman.com/downloads/ 접속 후 다운로드 및 설치

  • 설치 완료 후 앱을 실행
    • Create Workspace 혹은 Collections > 생성한 Workspace 혹은 Collections 접속 > + 클릭


📌 GET 요청 : 주소에 localhost:8000/blog/ 라고 입력하고 send 버튼을 누르면, 하단 부분에 응답이 표시됨!


📌 POST 요청 : 글을 적는 것도 가능

  1. 주소창 왼쪽의 선택 버튼을 눌러서 GET → POST 로 변경
  2. 그 아래에서 Body 를 선택하신 뒤에, raw 를 클릭
  3. 그 후에 raw 의 오른쪽에 있는 선택 버튼 (text 라고 되어있는) 을 눌러서 json 으로 변경
  4. 이제 빈칸에다가 {"title”: “제목", “content”: “내용”} (글을 쓰기 위해 필요한 값들) 이라고 작성 후, send 버튼 클릭
    글쓰기 완료!

  • 다시 GET 요청으로 조회하여 정상 등록 여부 확인

🎨 게시글 하나만 보기/수정/삭제 (PUT / DELETE 요청 작성)

📌 게시글 하나만 보는 과정은 또 다른 url 과 view 를 통해서 진행할 수 있도록 작성

  • (서버주소)/blog/ 로 요청을 보낸다면 글 리스트를 보는 것 (혹은 글을 작성) 이고,

  • (서버주소)/blog/(글 번호) 로 요청 보낸다면 해당 번호의 글 하나만 조회 (혹은 글을 수정/삭제)

    • 서버주소/blog/1 과 같은 형식, 여기서 1은 글의 id이자 pk
    • pk 이기 때문에 1 이라는 숫자만으로 글 하나를 특정할 수 있는 것이고, 해당 글을 보여주게 되는 것
  • blog/urls.py 에는 path("<int:pk>", BlogDetail.as_view()), 를 추가

    • 여기서 int:pk 는 글자 그대로의 url 이 아니라, url 에 int(정수형)인 값을 받을 건데,
      그 값의 이름을 pk 라고 해서 views.py 에서 변수로 쓸 수 있도록 하겠다는 뜻
      • 서버주소/blog/1 라는 주소로 요청이 온다면,
        이 1이라는 값을 pk 라고 이름 붙여서 views 에서 사용할 수 있도록 하겠다는 뜻
      • 서버주소/blog/2 라는 주소로 요청이 온다면,
        이 2이라는 값을 pk 라고 이름 붙여서 views 에서 사용할 수 있도록 하겠다는 뜻

blog/urls.py

from django.urls import path
from blog.views import BlogList, BlogDetail   # BlogDetail 함수 추가


urlpatterns = [
    path("", BlogList.as_view()),
    path("<int:pk>", BlogDetail.as_view()),   # 해당 경로 추가

]
  • 이제 BlogDetail 함수 작성

blog/views.py

class BlogDetail(APIView):
    permission_classes = [AllowAny]

    def get(self, request, pk): # url 에 있는 숫자를 pk 라는 변수로 받아옴
        article = Article.objects.get(pk=pk) # pk가 1이라면 글 id가 1인 글을 찾아서 가져옴
        serialized_article_data = ArticleSerializer(article).data # 해당 글을 직렬화함
        return Response(serialized_article_data, status=status.HTTP_200_OK) # 직렬화한 글 데이터를 응답으로 보냄

    def put(self, request, pk): # put 은 수정을 요청할 때 쓰는 Method 입니다
        article = Article.objects.get(pk=pk) # 기존의 글 데이터를 db 에서 찾아옴
        # 원래라면 여기서 권한 체크
        article_serializer = ArticleSerializer(article, data=request.data) # 기존의 글과 함께, 현재 유저가 보낸 수정할 데이터 (request.data) 를 명시
        if article_serializer.is_valid(): # 유효성 검사
            article_serializer.save() # 유효하다면 수정작업 완료
            return Response(article_serializer.data) # 수정한 글 데이터를 응답으로 보냄
        return Response(article_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        article = Article.objects.get(pk=pk) # 글을 db 에서 찾아옴
        # 원래라면 여기서 권한 체크
        article.delete() # 해당 글을 db 에서 삭제함
        return Response(status=status.HTTP_204_NO_CONTENT)

📌 Postman으로 확인

  • 아래와 같이 /글id번호 를 붙여서 보내면, 해당 글번호에 해당하는 것만 GET 하게 됨.

    • GET : 게시글 하나만 보기
  • methodPUT으로 변경하여 BODY 내용을 수정 후 보내면, 해당 글번호의 내용이 수정됨.

    PUT : 게시글 수정

  • methodDELETE로 변경하여 보내면, 해당 글이 삭제됨.

    DELETE : 게시글 삭제

    정상 삭제 여부 확인

post-custom-banner

0개의 댓글