DRF API : 지난 포스트에서 DRF 시리얼라이져를 사용해서 모델에 맞는 직렬화를 자동으로 해주고 API로 응답하는 방법을 정리했습니다. 이번에는 지난 정리에 이어서 DRF를 이용해서 CRUD에 대한 API를 생성하는 법을 공부하고 정리해보았습니다.
DRF 개념 정리
API 설계
다음과 같이 API를 설계했다고 가정하고 이를 어떻게 DRF로 구현하는지 공부해보았습니다.
Name | Method | Endpoint |
---|---|---|
Article 목록 조회 | GET | /articles/ |
Article 상세 조회 | GET | /articles/<int:article_id>/ |
Article 생성 | POST | /articles/ |
Article 수정 | PUT | /articles/<int:article_id>/ |
Article 삭제 | DELETE | /articles/<int:article_id>/ |
목록 조회 DRF API
urls.py
- DRF에서는 Template를 사용하지 않기 때문에 name이나 app_name을 적지 않아도 됩니다.
from django.urls import path
from . import views
app_name = "articles"
urlpatterns = [
path("", views.article_list, name="article_list"),
]
serializer.py
- ModelSerializer를 상속 받아서 시리얼라이져 클래스를 만듭니다.
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = "__all__"
views.py
- 조회는 GET method 이므로 @api_view([“GET”]) 데코레이터를 넣습니다.
- 데이터를 가져와서 시리얼라이져에 넣어서 Response로 반환해줍니다.
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import ArticleSerializer
from .models import Article
@api_view(["GET"])
def article_list(request):
articles = get_object_or_404(Article)
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
API 요청 결과
상세 조회 DRF API
urls.py
- 상세 요청은 URL 쿼리스트링으로 해당 id 값을 받아야 함으로 이에 맞게 `<int:article_pk>/`와 같이 variable routing을 넣어줍니다.
from django.urls import path
from . import views
app_name = "articles"
urlpatterns = [
path("", views.article_list, name="article_list"),
path("<int:article_pk>/", views.article_detail, name="article_detail"),
]
views.py
- 동일하게 데이터를 가져오는데 특정 pk의 데이터만 가져와서 시리얼라이져에 넣어주고 Response를 반환해줍니다.
@api_view(["GET"])
def article_detail(request, article_pk):
article = get_object_or_404(Article, pk=article_pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
API 요청
정상적인 요청
- 정상적으로 API가 호출되어 JSON 데이터를 받아오는 것을 확인 할 수 있습니다.
모델에 없는 데이터 요청
- 현재 모델에 없는 999번 데이터를 요청하면 404 Not Found가 뜨는 것을 확인 할 수 있습니다.
생성 DRF API
urls.py
- Endpoint를 동일하게 하고 방법만 POST로 변경하면 CREATE 요청하도록 설계했기 때문에 urls.py에서는 변경사항 없음
from django.urls import path
from . import views
app_name = "articles"
urlpatterns = [
path("", views.article_list, name="article_list"),
path("<int:article_pk>/", views.article_detail, name="article_detail"),
]
views.py
@api_view()
에서POST
를 추가POST
로 요청 받았을 때 시리얼라이져에 CREATE를 요청하고 결과를 반환하는 로직을 구성Serializer
는 모델에 맞게 데이터를 받아서 유효성 검사까지 해줌Serializer
의data
파라미터에request.data
인자를 넘기고serializer
인스턴스 생성serializer
인스턴스를is_valid()
로 유효성 검사 후,save()
메소드로 저장- Response에서
serializer.data
를 인자로 받고 이것을 반환 - 에러가 있는 경우
Response(serializer.errors, status=400)
로 반환
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
elif request.method == "POST":
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
API 요청
- method를 POST로 변경하고 API를 요청해봅니다.
- POST는 GET과 달리 query string으로 요청하지 않으므로 Body 탭을 클릭해서 원하는 형태로 생성 요청을 해줍니다.
- 정상적으로 요청이 되면 views에서 작성한대로 201 상태코드가 반환 됩니다.
- 잘못된 요청을 하면 views에서 작성한대로 400 코드를 반환 하는 것을 확인 할 수 있습니다.
HTTP 상태코드를 자동으로 반환
- 위에서는
return Response(serializer.errors, status=400)
를 추가적으로 명시함으로써 특정 상태를 반환해주었는데요. serializer.is_valid(raise_exception=True)
와 같이raise_exception=True
인자를 추가해주면 장고가 잘못된 상태에 대한 반환을 자동으로 알아서 해줍니다.
상태코드를 명시적으로 입력
Response(serializer.data, status=201)
처럼 Response에 상태코드를 201로만 입력할 수 있지만, 아래 방법을 통해서 상태코드의 의미를 조금 더 명확하게 명시해줄 수도 있습니다.Response(serializer.data, status=status.HTTP_201_CREATED)
처럼status.HTTP_201_CREATED
을 status 인자로 입력하면 누구나 어떤 상태인지 명확하게 이해할 수 있습니다.- status code는 장고에서 미리 정의해주었기 때문에 import만 해서 사용하면 됩니다.
from rest_framework import status
from rest_framework import status
@api_view(["GET", "POST"])
def article_list(request):
if request.method == "GET":
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
elif request.method == "POST":
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
삭제 DRF API
urls.py
- article 상세에서 method만 DELETE로 변경하는 것이기 때문에 urls.py에는 변경이 없음
views.py
@api_view(["GET", "DELETE"])
def article_detail(request, article_pk):
article = get_object_or_404(Article, pk=pk)
if request.method == "GET":
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == "DELETE":
article.delete()
data = {"delete": f"Article({article_pk}) is deleted."}
return Response(data, status=status.HTTP_200_OK)
api 요청
- DELETE 요청을 날려봅니다.
- 삭제되었을 때 JSON이 출력 됩니다.
- 주의 : URL에 trail slash(/)을 마지막에 제대로 붙여줘야 정상 작동 합니다.
- GET으로 삭제한 ID를 조회하면 404가 뜨는 것을 확인할 수 있습니다.
수정 DRF API
serializer = ArticleSerializer(article, data=request.data, partial=True)
- serializer에 data만 binding시키면 CREATE로 작동하고 모델에서 가져온 데이터를 같이 binding해주면 UPDATE로 작동합니다.
- 이는 Model form에서 data만 넣으면 CREATE이지만 instance를 넣으면 UPDATE로 작동하는 것과 유사한 방식임을 알 수 있습니다.
- 시리얼라이져를 선언할 때,
field = '__all__'
로 선언했기 때문에 UPDATE 요청에는 모든 필드의 값을 다시 입력 받아야합니다. - 하지만
partial=True
인자를 시리얼라이져에 전달하면 모든 필드의 값을 받지 않고, 일부 필드의 값만 받아도 수정이 가능하게 해줍니다. - 예를 들어, Article 모델에 title, content, image 필드가 있다고 가정할 때, 클라이언트가title 필드만 업데이트하려는 경우, partial=True 없이는 content와 image도 함께 전송해야 하지만, partial=True를 사용하면 title 필드만 전송하고 나머지 필드는 그대로 유지할 수 있습니다.
views.py
@api_view(["GET", "PUT", "DELETE"])
def article_detail(request, article_pk):
article = get_object_or_404(Articles, pk=article_pk)
if request.method == "GET":
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == "PUT":
article = get_object_or_404(Articles, pk=article_pk)
serializer = ArticleSerializer(article, data=request.data, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
elif request.method == "DELETE":
article.delete()
data = {"delete": f"Article({article_pk}) is deleted."}
return Response(data, status=status.HTTP_200_OK)
마치며
생성, 조회, 수정, 삭제에 대한 API를 DRF로 생성해보았습니다. DRF가 참 편하네요.. 역시 만들어져 있는 걸 가져다 쓰는 게 참 편한 것 같습니다.
또한 API를 만들면서 느낀점인데 PostMan을 통해서 손쉽게 결과를 보면서 연습하니까 굉장히 좋은 것 같습니다.
다만 주의점은 브라우저에서는 trail slash(/)를 명시적으로 입력하지 않아도 자동으로 붙여서 요청을 보내주지만 PostMan에서는 trail slash를 명시적으로 붙여줘야 정상 작동 합니다.
해당 사실을 몰라서 코드를 뜯어보면서 1시간을 보낸 것 같네요…ㅎ
참고하면 좋은 글
장고 시작하기 1 : 장고 환경 구성하기
장고를 시작하기 위해서는 장고를 설치하고 장고만의 환경을 만들어줘야 합니다. 장고가 정상적으로 설치되고 환경 구성이 완료되면 장고 로켓 화면을 볼 수 있습니다
장고 시작하기 2 : startapp으로 app 추가
[장고 시작하기] 2 번째 시리즈 : 지난 시간에 Django 환경 세팅하는 것에 대해서 공부하고 정리해보았습니다. 이번에는 실제 장고로 웹 페이지를 구성하기 위해서 ‘startapp으로 app 추가’ 하는 법에 대해서 공부하고 정리해 보았습니다.
장고 시작하기 3. 장고 extend include 사용하여 template 관리
장고 extend, include : 각 페이지 마다 html을 만들 수 있지만 페이지가 조금만 늘어나도 html을 구조화해서 재사용하고 싶어집니다. 이를 구현하기 장고에서는 extend와 include 태그를 제공해줍니다.
장고 시작하기 4. 장고 화면 꾸미기 기초 (장고 static 파일 관리)
CSS, 글꼴, 이미지 파일, 자바스크립트 파일 등 웹 사이트를 꾸미기 위한 정적 파일들을 있습니다. Django에서는 이런 파일들을 static 디렉토리에서 관리합니다. 하지만 이 static 디렉토리가 자동으로 생성되지 않습니다.
장고 시작하기 5 : 서버와 클라이언트 데이터 주고받기
장고 기초에서의 제일 중요한 내용이 “서버와 클라이언트가 어떻게 데이터를 주고 받는지” 인 것 같습니다. GET은 URL 뒤에 ?쿼리 형식으로 데이터를 보내고, 작은 데이터를 서버로 보낼 때 사용합니다. POST는 INSERT, UPDATE 때 사용되며, 데이터가 BODY에서 전송됩니다.두 방식 모두 form 태그를 사용하는데요
장고 시작하기 6 : 장고 DTL 동적 페이지 구축을 위한 기본 문법 1
Django Template Language (DTL)은 동적 웹 페이지를 구축하기 위한 강력한 도구입니다. 이를 통해 개발자는 Django views에서 제공하는 데이터를 기반으로 HTML로 동적으로 렌더링되는 템플릿을 만들 수 있습니다.
장고 시작하기 7 : 장고 URL, variable routing, name 파라미터
“장고 URL”을 다루는 정말 편리한 기술들이 많습니다. 모르면 암호 같은 표기인데 알고 나면 너무 편리한 기능들인 거죠. 이런 기능들에는 Variable Routing과 Naming URL 패턴이 있습니다.
장고 시작하기 8 : 장고 Model 생성하기
“장고 Model”은 데이터베이스 테이블을 나타내는 Python 클래스입니다. 데이터베이스에 저장된 데이터의 구조와 동작을 정의할 수 있는거죠. 장고 Model의 장점은 데이터베이스에 구애 받지 않는다는 점 입니다.
장고 시작하기 9. 장고 ORM 개념, 장고 Shell에서 테스트하기
장고 ORM (Object-Relational Mapping)은 개발자가 Python 객체를 사용하여 데이터베이스와 상호 작용할 수 있게 해주는 기능으로, SQL 쿼리를 직접 작성하지 않아도 단순하게 DB 작업을 할 수 있게 만들어 줍니다.
장고 시작하기 10. 장고 CRUD
CRUD는 아시다시피 생성(Create), 읽기(Read), 업데이트(Update), 삭제(Delete)를 의미하며 데이터베이스의 데이터에 대해 수행할 수 있는 4가지 기본 작업을 나타내는데요. 이번 포스트에서는 장고 CRUD 는 어떻게 진행
장고 시작하기 12 : 장고 Form
Django의 Form 클래스는 HTML form과 관련된 태그들을 생성하고 웹 애플리케이션에서 해당 데이터를 처리하는 편리한 방법을 제공해줍니다. 사용자가 귀찮게 반복해서 작성해야 하는 HTML form 태그 부분을 장고에서 지원해 주는 거죠.