<백엔드>
- 댓글 좋아요 기능
- 지난 프로젝트 때 구현했던 거라 금방 했다
# articles/urls.py
from django.urls import path
from . import views
urlpatterns = [
...
path("like/<int:comment_id>/", views.LikeView.as_view(), name="like_view"),
...
]
# articles/view.py
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Comment
class LikeView(APIView):
def post(self, request, comment_id):
"""댓글 좋아요 누르기"""
comment = get_object_or_404(Comment, id=comment_id)
if request.user in comment.like.all():
comment.like.remove(request.user)
return Response("dislike", status=status.HTTP_200_OK)
else:
comment.like.add(request.user)
return Response("like", status=status.HTTP_200_OK)
- 게시글 북마크 기능
- 좋아요랑 거의 똑같다
# articles/urls.py
from django.urls import path
from . import views
urlpatterns = [
...
path("bookmark/<int:article_id>/",
views.BookmarkView.as_view(), name="bookmark_view"),
...
]
# articles/view.py
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Comment
class BookmarkView(APIView):
def post(self, request, article_id):
"""게시글 북마크 하기"""
article = get_object_or_404(Article, id=article_id)
if request.user in article.bookmark.all():
article.bookmark.remove(request.user)
return Response("unbookmark", status=status.HTTP_200_OK)
else:
article.bookmark.add(request.user)
return Response("bookmark", status=status.HTTP_200_OK)
- 좋아요순 댓글 목록 불러오기
- 이걸 구현하려고 했는데 처음에는 order_by에 like를 넣어서 했다
- 그랬더니 like가 추가 될 때마다 똑같은 객체가 중복으로 불러와져서 이건 아니라는 걸 깨달았다
- 생각해보니까 like는 좋아요 누른 사람들 목록이 담겨있는 거고 내가 원하는 건 좋아요 개수대로 정렬하는 거였다
- 그래서 article 모델에 like_count 함수를 만들었는데 각 댓글 객체들에 적용을 해야 하는데 이 함수로는 활용하기가 어려울 거 같았다
- 그 다음에는 시리얼라이저에 메소드필드였나? 그걸로 like_count를 넣었는데 그것도 정렬하려면 어떻게 해야 할지 모르겠어서 그냥 프론트엔드에서 정렬하기로 했다
- 그래서 그냥 댓글 목록을 모두 불러오는 기능을 구현했다
# articles/urls.py
from django.urls import path
from . import views
urlpatterns = [
...
path("comments/", views.CommentsView.as_view(), name="main_comment_view"),
...
]
# articles/view.py
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Article, Comment
class CommentsView(APIView):
def get(self, request, article_id=None):
"""댓글 보기"""
if article_id:
articles = get_object_or_404(Article, id=article_id)
comments = articles.comments.all()
else:
comments = Comment.objects.all()
serializer = CommentSerializer(comments, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
...
- 원래 댓글 조회 기능은 article id를 가져와서 그 게시글에 달린 댓글들만 조회하는 거였다
- 그래서 매개변수 article_id=None으로 해서 article_id가 없이 get요청이 들어오면 else문이 실행되도록 했다
- 게시글 상관없이 모든 댓글들을 불러오는 것이다
<프론트엔드>
- 우선 메인페이지를 구현해야 하기 때문에 index.html을 수정했다
- 네모박스 2개를 만들어서 댓글모음이랑 게시글 모음을 그 박스에 담도록 했다
<!--index.html-->
...
<div class="container text-center mt-5">
<div class="row gap-5">
<div class="col pt-3 pb-3" id="most-like-comment" style="background-color:aqua">
좋아요를 가장 많이 받은 댓글
</div>
<div class="col pt-3 pb-3" id="recently-article" style="background-color:aqua">
최근에 올라온 게시글
</div>
</div>
</div>
...
- 좋아요 개수순 댓글 10개씩 가져오기
- 우선 api.js에 백엔드와 통신해서 댓글목록을 가져오는 함수를 만들었다
// api.js
// 댓글 전체 목록 불러오기
async function getComments() {
const response = await fetch(`${backend_base_url}/api/articles/comments/`, {
method: "GET"
});
response_json = await response.json();
return response_json;
}
- 그리고 loader.js에 아까 html에서 이어서 만들 카드 목록을 만드는 함수를 작성했다
// loader.js
// 댓글 목록 UI
function commentList(comments, list_div) {
comments.forEach(comment => {
const newCol = document.createElement("div");
newCol.setAttribute("class", "col")
newCol.setAttribute("onclick", `articleDetail(${comment.pk})`)
const newCard = document.createElement("div")
newCard.setAttribute("class", "card h-100")
newCard.setAttribute("id", comment.pk)
newCol.appendChild(newCard)
const newCardBody = document.createElement("div")
newCardBody.setAttribute("class", "card-body")
newCard.appendChild(newCardBody)
const newCardTile = document.createElement("h5")
newCardTile.setAttribute("class", "card-title")
newCardTile.innerText = comment.comment
newCardBody.appendChild(newCardTile)
const newCardlike = document.createElement("p")
newCardlike.setAttribute("class", "card-text")
newCardlike.innerText = `좋아요수 : ${comment.like_count}`
newCardBody.appendChild(newCardlike)
list_div.appendChild(newCol)
})
}
- 그리고 이제 index.js에서 가져온 정보를 띄운다
//메인페이지 좋아요순 댓글 가져오기
window.onload = async function () {
const like_comments = await getComments()
like_comments.sort((x, y) => y.like_count - x.like_count)
const comments_list = document.getElementById("most-like-comment")
commentList(like_comments.slice(0, 10), comments_list)
}
- 다행히 구글링을 하니까 정렬하고 10개만 가져오는 방법을 찾을 수 있었다
- sort부분이 정렬하는 건데 x - y 하면 오름차순이고 y - x 하면 내림차순이다
- 나는 좋아요 개수가 많은 순으로 나열하고 싶기 때문에 내림차순으로 했다
- 근데 그냥 좋아요 개수순으로 하면 메인페이지가 거의 항상 똑같을 수 있기 때문에 나중에 24시간 내에 눌린 좋아요 개수를 따로 체크해서 그걸 반영하는 리팩토링을 하면 좋을 것 같다
- 지금은 다른 기능 구현도 아직 못 한 상태니까 이 정도에서 만족하는 걸로...
- 그리고 10개씩 자르는 건 slice 함수로 구현했다 Good~
- 최신순 게시글 10개씩 가져오기
- 좋아요순 댓글 가져오는 거랑 거의 비슷하다
// api.js
// 전체 게시글 목록 불러오기
async function getArticles() {
const response = await fetch(`${backend_base_url}/api/articles`, {
method: "GET"
});
response_json = await response.json();
return response_json;
}
// loader.js
// 게시글 목록 UI
function articleList(articles, list_div) {
articles.forEach(article => {
const newCol = document.createElement("div");
newCol.setAttribute("class", "col")
newCol.setAttribute("onclick", `articleDetail(${article.pk})`)
const newCard = document.createElement("div")
newCard.setAttribute("class", "card h-100")
newCard.setAttribute("id", article.pk)
newCol.appendChild(newCard)
const articlePhoto = article.photos[0]?.file;
const articleImage = document.createElement("img");
articleImage.setAttribute("class", "card-img-top")
if (articlePhoto) {
articleImage.setAttribute("src", `${articlePhoto}`);
} else {
articleImage.setAttribute("src", "https://cdn11.bigcommerce.com/s-1812kprzl2/images/stencil/original/products/426/5082/no-image__12882.1665668288.jpg?c=2")
}
articleImage.setAttribute("onclick", `uploadPhoto(${article.pk})`);
newCard.appendChild(articleImage)
const newCardBody = document.createElement("div")
newCardBody.setAttribute("class", "card-body")
newCardBody.setAttribute("onclick", `articleDetail(${article.pk})`)
newCard.appendChild(newCardBody)
const newCardTile = document.createElement("h5")
newCardTile.setAttribute("class", "card-title")
newCardTile.innerText = article.title
newCardBody.appendChild(newCardTile)
const newCardtime = document.createElement("p")
newCardtime.setAttribute("class", "card-text")
newCardtime.innerText = `생성시간 : ${article.created_at}`
newCardBody.appendChild(newCardtime)
list_div.appendChild(newCol)
})
}
- 이미지 넣는 방식이 DB와 소통하는 게 아니라 클라우드와 소통하는 방식이라 image 넣는 부분은 다른 분이 구현해 주셨다
//메인페이지 최신순 게시글 가져오기
window.onload = async function () {
...
const recently_articles = await getArticles()
...
const article_list = document.getElementById("recently-article")
articleList(recently_articles.slice(0, 10), article_list)
}
- 위에서 작성했던 좋아요순 게시글 가져오는 함수에서 article 부분만 추가했다
- 프론트엔드 구현된 이미지
- 카드가 원하는대로 붙질 않아서...기능 구현은 됐으나 보기가 안 좋은 상태다