이 게시글은
김준태 강사님
의DRF - Basic
강의를 듣고 작성하였습니다.
📢 수업 목표
DRF
로 간단한 블로그 만들기
Django Project 초기 셋팅 글을 참고하여 초기 셋팅 진행
📌 블로그를 만들기 위해 필요한 것은?
- 이용자가 글을 쓸 수 있고, 이용자가 쓴 글이 계속해서 쌓이면서 이를 다른 사람이 볼 수 있어야 함.
📌 바로 데이터베이스
가 필요
- 어떤 이용자 혹은 게시글이 있는지를 데이터베이스라는
정보 저장소
를 통해서 확인하고, 사용자나 게시글을 해당 정보 저장소에 추가하는 형태로 작업이 이루어지게 됩니다.
📌 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
: 글 저장 공간 만들기
📌 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 이라는 이름으로 하나의 테이블을 만들 수 있게 됨.
CharField
(단어 / 짧은 텍스트), TextField
(긴 글), DateTimeField
(시간) 등이 있음.
auto_now_add
: 처음 추가될 때 자동으로 현재 시각을 받아오겠다는 뜻- auto_now : 수정될 때마다 자동으로 현재 시각을 받아오겠다는 뜻
→ 이렇게 하면 사용자는 title, content 만 작성하면 되고, 작성 시각 등은 django 가 알아서 처리해줌.- id 를 굳이 직접 만들지 않은 이유는, primary key 로 쓸 필드는 굳이 직접 만들지 않아도 django 가 알아서 테이블 내에 id (혹은 pk) 라는 이름으로 필드를 만들어 주기 때문!
serializers
: 글에서 어떤 정보를 보여주거나 / 받을지를 지정
📌 request
와 response
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
: 주소로 들어왔을때 실행될 내부의 작업 (db에 저장, db에서 가져오기) 을 지정
📌 views : 이용자 (프론트엔드) 로부터 요청 (+ 데이터) 을 받았을 때, 작동할 함수를 작성하는 곳
실생활 비유
- 실생활에 비유를 해보자면, 우리가 동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서 공무원에게 발급 서류를 제출함.
- 그러면 공무원은 그 서류를 토대로 일정한 작업을 한 뒤에, 주민등록증을 발급해주게 됨.
위의 예시를 웹사이트에 비유
- "우리가
(웹사이트 이용자가)
동사무소에 주민등록증을 발급하러 가면, 민증 발급 창구에 가서(웹사이트 URL로)
공무원에게 발급 서류를 제출(요청, Request)
함.- 그러면 공무원
(django)
은 그 서류(요청에 포함된 데이터, Request Data)
를 토대로
일정한 작업(views.py 에 작성된 함수를 실행)
을 한 뒤에, 주민등록증을 발급해주게 됨.(응답, Response)
"
📌 django 를 통해 글을 쓰는 과정 (POST 요청)
안내
- 웹사이트 이용자가 (글을 작성하려면),
글 작성 URL
로글 데이터
를 포함해서요청
을 보냄- 그러면 django 는 해당 url 에 대응하는
views.py 내부의 함수를 실행
- 이 views.py 내부의 함수는
글 데이터를 serializer 를 통해서 역직렬화
+해당 글을 db 에 저장
- 글 작성 성공 여부를
응답
으로 전달해주게 됨.
📌 지금은 우선 글을 가져오는 것 (GET 요청)
부터 먼저 진행!
- 웹사이트 이용자가
글 가져오기 URL 로 요청
을 보내면,- django 는 해당 url 에 대응하는
views.py 내부의 함수를 실행
하고,- 이 함수는 db 에 저장된 글들을 serializer 를 통해서
직렬화
해서글 데이터를 응답
으로 전달
- db 에 저장된 글을 가져오고
- 글을 직렬화하고,
- 그렇게 직렬화한 글 데이터가 포함된 응답을 return 하는 함수를 작성
📌 CBV (Class Based View)
- 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) # 직렬화한 데이터 응답으로 보내기
def get(self, request):
이라고 작성하고, 그 아래에 글을 가져와서 응답으로 전달하는 과정을 작성.
- 이렇게 하면 자동으로 get 요청에 대응해서 지금 작성한 함수가 실행됨.
📌 django orm
전체 데이터
가져오기
(models.py 에 작성한 모델명).objects.all()
# Article.objects.all() # Article 테이블에서 모든 데이터를 다 가져옴.
일부 데이터
가져오기(데이터여러개
)
(models.py 에 작성한 모델명).objects.filter(필드명="값")
Article.objects.filter(title="안녕") # Article 테이블에서 제목에 '안녕' 이라고 되어있는 '데이터들'만 가져옴
특정 데이터 하나
가져오기 (데이터 딱 하나)
(models.py 에 작성한 모델명).objects.get(필드명="값")
Article.objects.get(pk=1) # Article 테이블에서 id 가 1 인 (첫번째로 작성된) 글을 가져옴
📌 데이터 직렬화
(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.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
- 만약 위의 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
: 관리자 페이지
- 이를 이용하면 현재 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번부터 부여됨.
- 이제 게시글을 작성하기 위한 코드를 작성할텐데, 지금 게시글을 저장할 공간과 필요한 내용 (model/serializers) 는 이미 작성되어 있으니 views.py 만 추가로 작성
POST 요청
과 관련해서 앞서 언급했던 내용을 한번 더 정리
- 웹사이트 이용자가 (글을 작성하려면),
글 작성 URL (현재는 글 작성 URL, 가져오기 URL 이 동일함)
로글 데이터
를 포함해서요청
전송- 그러면 django 는 해당 url 에 대응하는
views.py 내부의 함수를 실행
- 이 views.py 내부의 함수는
글 데이터를 serializer 를 통해서 역직렬화
+해당 글을 db 에 저장
- 글 작성 성공 여부를
응답
으로 전달
위의 과정도 아래와 같은 코드로 표현 가능
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
: 우리가 만든 서버로 직접 get, post 요청 등을 보내볼 수 있도록 도와주는 프로그램
https://www.postman.com/downloads/ 접속 후 다운로드 및 설치
Create Workspace 혹은 Collections
> 생성한Workspace 혹은 Collections
접속 >+
클릭
📌 GET 요청 : 주소에 localhost:8000/blog/ 라고 입력하고 send 버튼을 누르면, 하단 부분에 응답이 표시됨!
📌 POST 요청 : 글을 적는 것도 가능
- 주소창 왼쪽의 선택 버튼을 눌러서 GET → POST 로 변경
- 그 아래에서 Body 를 선택하신 뒤에, raw 를 클릭
- 그 후에 raw 의 오른쪽에 있는 선택 버튼 (text 라고 되어있는) 을 눌러서 json 으로 변경
- 이제 빈칸에다가
{"title”: “제목", “content”: “내용”}
(글을 쓰기 위해 필요한 값들)
이라고 작성 후, send 버튼 클릭
→글쓰기 완료!
- 다시
GET
요청으로 조회하여 정상 등록 여부 확인
📌 게시글 하나만 보는 과정은 또 다른 url 과 view 를 통해서 진행할 수 있도록 작성
(서버주소)/blog/
로 요청을 보낸다면 글 리스트를 보는 것 (혹은 글을 작성)
이고,
(서버주소)/blog/(글 번호)
로 요청 보낸다면 해당 번호의 글 하나만 조회
(혹은 글을 수정/삭제)
- 서버주소/blog/1 과 같은 형식, 여기서 1은 글의 id이자 pk
- pk 이기 때문에 1 이라는 숫자만으로 글 하나를 특정할 수 있는 것이고, 해당 글을 보여주게 되는 것
blog/urls.py 에는 path("<int:pk>", BlogDetail.as_view())
, 를 추가
- 서버주소/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()), # 해당 경로 추가
]
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 : 게시글 하나만 보기
method
를 PUT
으로 변경하여 BODY
내용을 수정 후 보내면, 해당 글번호의 내용이 수정됨.
PUT : 게시글 수정
method
를 DELETE
로 변경하여 보내면, 해당 글이 삭제됨.
DELETE : 게시글 삭제
정상 삭제 여부 확인