웹 애플리케이션에서 가장 흔하게 사용되는 기능 중 하나가 바로 게시판이죠!
사용자가 게시글을 작성하고, 읽고, 수정하고, 삭제할 수 있는 이러한 CRUD(Create, Read, Update, Delete) 기능은 웹 개발의 기본입니다.
이번 포스팅에서는 Django의 강력한 기능 중 하나인 Django REST Framework(DRF)를 활용하여 REST API 기반의 게시판 기능을 구현하는 방법을 다룰 것입니다.
또한, API의 테스트와 검증을 위해 Postman이라는 API 테스트 도구를 사용해, 실시간으로 API를 호출하고 결과를 확인하는 과정도 함께 살펴보겠습니다!
프로젝트 준비
Postman 설치하기
https://www.postman.com/downloads/
Download Postman | Get Started for Free
Try Postman for free! Join 35 million developers who rely on Postman, the collaboration platform for API development. Create better APIs—faster.
www.postman.com
프로젝트 및 앱 생성하기
1. 가상 환경 설치 및 활성화
$ python -m venv venv
$ source venv/Scripts/activate
2. django, drf 설치
$ pip install django djangorestframework
# 패키지 설치 후 requirements.txt에 기록
$ pip freeze > requirements.txt
3. my_api 프로젝트 생성
$ django-admin startproject my_api .
4. articles 앱 생성
$ python manage.py startapp articles
5. settings.py의 INSTALLED_APPS에 articles, rest_framework 등록
# my_api/settings.py
INSTALLED_APPS = [
'articles',
'rest_framework',
# . . .
]
Article 모델 작성 및 URL 정리
1. Article 모델 작성
# articles/models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=10)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
2. URL 분리하기
# my_api/urls.py
from django.urls import path, include
urlpatterns = [
# . . .
path('api/v1/', include('articles.urls')),
]
3. articles 폴더에 urls.py 생성
# articles/urls.py
from django.urls import path
from . import views
urlpatterns = [
]
템플릿을 사용하지 않으므로 app_name과 URL 등록 시 name도 설정하지 않습니다!
DB에 적용하기
$ python manage.py makemigrations
$ python manage.py migrate
샘플 레코드 생성하기
<Fixtures 파일이 있는 경우>
1. article.json 파일에서 읽어오기
$ python manage.py loaddata article.json
<Fixtures 파일이 없는 경우>
1. django-seed 활용하기
$ pip install django-seed psycopg2
2. INSTALLED_APPS에 추가
# my_api/settings.py
INSTALLED_APPS = [
# . . .
'django_seed',
# . . .
]
3. 레코드 생성
$ python manage.py seed --number 10 articles
샘플 레코드 생성 과정을 더 자세히 알고 싶으시거나, 에러가 발생하신 분은 아래 포스팅을 참고하세요!
[Django] Django seed로 테스트 데이터 자동 생성하는 법
django로 개발할 때, 테스트 데이터가 필요할 때가 있습니다.이 때 손수 하나하나 넣어주는 건 시간적으로 매우 비효율적이죠. 그럴 때 쓸 수 있는 테스트 데이터 자동 생성 라이브러리!django-seed
joungnx123.tistory.com
Article(single model) CRUD
URL과 Request
GET | POST | PUT | DELETE | |
articles/ | 전체 글 조회 | 글 작성 응답 코드 : 201 |
||
articles/1/ | 1번 글 조회 | 1번 글 수정 | 1번 글 삭제 응답 코드 : 204 |
- 유효성 검증 실패 시 400 응답 코드 반환
게시글 전체 조회
1. 게시글 전체 조회를 위한 serializer 작성
- articles 폴더 아래에 serializers.py 파일 생성
# articles/serializers.py
from rest_framework import serializers
from .models import Article
class ArticleListSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('id', 'title',)
2. 게시글 전체 조회 URL
# articles/urls.py
urlpatterns = [
# 전체글 조회(GET) 및 새 게시글 작성(POST)
path('articles/', views.article_list),
]
3. 게시글 전체 조회 View 함수
- 필요한 모듈들 import 하기
# articles/views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleListSerializer
- article_list 함수 작성
@api_view(['GET',])
def article_list(request):
articles = Article.objects.all()
serializer = ArticleListSerializer(articles, many=True)
return Response(serializer.data)
4. Postman으로 확인
- http://127.0.0.1:8000/api/v1/articles/ 주소에 GET 요청을 보낸 뒤 결과 확인

게시글 상세 조회
1. 게시글 상세 조회를 위한 serializer 작성
- ArticleSerializer 정의
# articles/serializers.py
# . . .
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
2. 게시글 상세 조회 URL
# articles/urls.py
urlpatterns = [
# . . .
# 글 상세 조회(GET), 수정(PUT), 삭제(DELETE)
path('articles/<int:article_pk>/', views.article_detail),
]
3. 게시글 상세 조회 View 함수
- article_detail 함수 작성
# articles/views.py
from .serializers import ArticleListSerializer, ArticleSerializer
@api_view(['GET',])
def article_detail(request, article_pk):
article = Article.objects.get(pk=article_pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
4. Postman으로 확인
- http://127.0.0.1:8000/api/v1/articles/1/ 주소에 GET 요청을 보낸 뒤 결과 확인

게시글 작성하기
1. article_list 함수에서 GET/POST를 구분해서 작성
@api_view(['GET', 'POST', ]) # <--- POST 추가
def article_list(request):
if request.method == 'GET':
articles = Article.objects.all()
serializer = ArticleListSerializer(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)
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/articles/ 주소에 POST 요청을 보낸 뒤 결과 확인

게시글 수정 및 삭제하기
1. article_detail 함수 완성하기
@api_view(['GET', 'PUT', 'DELETE',]) # <------ PUT, DELETE
def article_detail(request, article_pk):
article = Article.objects.get(pk=article_pk)
if request.method == 'GET':
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ArticleSerializer(article, request.POST, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
elif request.method == 'DELETE':
article.delete()
return Response(
{'delete': f'{article_pk}번 삭제'},
status=status.HTTP_204_NO_CONTENT
)
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/articles/1/ 주소에 PUT 요청을 보낸 뒤 결과 확인

- http://127.0.0.1:8000/api/v1/articles/1/ 주소에 DELETE 요청을 보낸 뒤 결과 확인

1:N Relation
Comment 모델 작성
# articles/models.py
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
DB에 적용
$ python manage.py makemigrations
$ python manage.py migrate
Comment 샘플 데이터 생성
$ python manage.py seed --number 5 articles
URL과 Request
GET | POST | PUT | DELETE | |
comments/ | 댓글 목록 조회 | |||
comments/1/ | 댓글 상세 조회 | 댓글 수정 | 댓글 삭제 | |
articles/1/comments/ | 댓글 생성 |
댓글 목록 조회
1. CommentSerializer 정의
# articles/serializers.py
from .models import Article, Comment
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
2. URL 작성
# articles/urls.py
urlpatterns = [
# . . .
# 전체 댓글 조회(GET)
path('comments/', views.comment_list),
]
3. comment_list 함수 작성
# articles/views.py
from .models import Comment
from .serializers import CommentSerializer
@api_view(['GET', ])
def comment_list(request):
comments = Comment.objects.all()
serializer = CommentSerializer(comments, many=True)
return Response(serializer.data)
4. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/ 주소에 GET 요청을 보낸 뒤 결과 확인

단일 댓글 조회
1. URL 작성 -> 수정/삭제에도 사용
# articles/urls.py
urlpatterns = [
# . . .
# 단일 댓글 조회(GET), 수정(PUT), 삭제(DELETE)
path('comments/<int:comment_pk>/', views.comment_detail),
]
2. comment_detail 함수 작성
@api_view(['GET', ])
def comment_detail(request, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
serializer = CommentSerializer(comment)
return Response(serializer.data)
3. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/1/ 주소에 GET 요청을 보낸 뒤 결과 확인

댓글 생성
1. URL 작성 -> Comment 테이블에 저장하려면 연관된 Article에 대한 정보가 필요
# articles/urls.py
urlpatterns = [
# . . .
# 댓글 생성 => Foreignkey에 해당하는 article 에 대한 정보가 필요
path('articles/<int:article_pk>/comments/', views.comment_create),
]
2. Comment.article 을 유효성 검증에서 제외시키기
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
read_only_fields = ('article', ) # <=== 추가
3. comment_create 함수 작성
@api_view(['POST', ])
def comment_create(request, article_pk):
article = Article.objects.get(pk=article_pk)
serializer = CommentSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
# article 인스턴스 추가 설정
serializer.save(article=article)
return Response(serializer.data, status=status.HTTP_201_CREATED)
4. Postman으로 확인
- http://127.0.0.1:8000/api/v1/articles/2/comments/ 주소에 POST 요청을 보낸 뒤 결과 확인

댓글 삭제, 수정
1. comment_detail 함수 수정
@api_view(['GET', 'PUT', 'DELETE', ])
def comment_detail(request, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
if request.method == 'GET':
serializer = CommentSerializer(comment)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = CommentSerializer(comment, request.data, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
elif request.method == 'DELETE':
comment.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/1/ 주소에 PUT 요청을 보낸 뒤 결과 확인

- http://127.0.0.1:8000/api/v1/comments/1/ 주소에 DELETE 요청을 보낸 뒤 결과 확인

댓글 조회 시 article의 정보를 추가하기 (참조)
1. CommentSerializer 수정
class CommentSerializer(serializers.ModelSerializer):
class ArticleTtitleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('title', )
article = ArticleTtitleSerializer(read_only=True)
class Meta:
model = Comment
fields = '__all__'
# read_only_fields = ('article', )
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/ 주소에 GET 요청을 보낸 뒤 결과 확인

게시글 조회 시 댓글 목록 추가하기 (역참조)
1. ArticleSerializer 수정
class ArticleSerializer(serializers.ModelSerializer):
class CommentDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id', 'content',)
comment_set = CommentDetailSerializer(read_only=True, many=True)
class Meta:
model = Article
fields = '__all__'
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/2/ 주소에 GET 요청을 보낸 뒤 결과 확인

댓글 개수 추가
1. ArticleSerializer 수정
class ArticleSerializer(serializers.ModelSerializer):
class CommentDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('id', 'content',)
comment_set = CommentDetailSerializer(many=True, read_only=True)
# 댓글 개수 필드 추가
comment_count = serializers.IntegerField(source='comment_set.count', read_only=True)
class Meta:
model = Article
fields = '__all__'
2. Postman으로 확인
- http://127.0.0.1:8000/api/v1/comments/2/ 주소에 GET 요청을 보낸 뒤 결과 확인

'Study > Django' 카테고리의 다른 글
[Django] Locust를 활용한 부하 테스트(Load Testing) (6) | 2024.11.08 |
---|---|
[Django] Django에서 올바르게 404 not found 에러 응답하기 (1) | 2024.10.17 |
[Django] Django seed로 테스트 데이터 자동 생성하는 법 (2) | 2024.10.16 |
[Django] Django로 CRUD를 구현해보자 (2) | 2024.09.28 |