[ PROJECT ] 카카오프렌즈샵 클론코딩 - #04 ‘게시물 댓글의 좋아요 수가 가장 큰 댓글 가져오기’

Hailee·2020년 12월 20일
0

[ PROJECT ]

목록 보기
8/16
post-thumbnail

우리 귀여운 카카오프렌즈 샵의 '오늘' 탭.
첫 접속(index) 시 보여주는 화면은 게시물들의 목록이다.

게시물마다 보이는 대표 댓글의 기준을 무엇으로 해야 할까.. 하다가
좋아요 수가 가장 많은 댓글을 기준으로 보여주기로 했다.

하지만 우리 데이터베이스 구조상, 암만 생각해도 모르겠는거.. 🤯
Boards 테이블을 Comments 테이블이 참조받고 있으며, Comments 테이블을 CommentLikes 테이블이 참조받고 있는 상황.
하지만 CommentLikes 테이블은 Members와 연결되어있는, MemberComments 사이의 중간 테이블.

CommentLikes 테이블에서 해당 board_id를 가진 comments들 중 좋아요 수 sum 한 값이 가장 큰 comment_id를 어떻게 가져올 것인가? CommentLikes 내에서 board_id랑 어떻게 연결할건데!??!!?

밤새 스트레스를 받던 나는, 그냥 created_at기준으로 가장 최근에 생성된 댓글을 보여주기로 했다.
하지만 이건 정말 우연의 일치로 밀리초까지 동시에 입력한 댓글이 있을 수도 있는것이고.. 시간이 pk가 될 수는 없으니까
(물론 전 회사에서는 그런 경우도 있었지만 지금 댓글에서 시간이 pk 역할을 할 수 있다는건 말이 안되쟈나)

결국 못참고 위코드 커뮤니티에 질문글을 올렸다.

안녕하세요, 15기 유하람입니다.
인스타그램에서 게시물 목록을 보면 가장 좋아요 수가 큰 댓글 한두개를 보여주듯이,
해당 게시물의 가장 좋아요 수가 큰 댓글을 가져오고 싶은 상황에서 도저히 방법이 떠오르지 않아서 질문드립니다.
저희 프로젝트의 게시물 관련 모델링 구조는 다음과 같습니다.


boards 테이블을 참조하는 comments테이블이 있으며, 어느 사용자가 좋아요를 눌렀는지 알 수 있는 users와 comments 테이블 사이에 commentlikes라는 테이블이 존재합니다


board_id에 대한 정보만 있다고 가정했을 때, 어떻게 하면 commentlikes테이블에서 해당 board_id를 가진 comments들의 sum을 구할 수 있을지 궁금합니다.

SELECT * FROM commentlikes
WHERE comment_id = (
   SELECT id FROM comments
   WHERE board_id = 3 AND created_at = (
      SELECT MAX(created_at) FROM comments 
      WHERE board_id=3
   )
)

지금 모델링 구조에서는 정 방법이 떠오르지 않아서, 한 게시물당 가장 최근에 작성된 댓글 하나만 보여준다고 가정했을 때 위 쿼리처럼 생각하면 가능은 하지만, max(created_at)는 유일한 값이 될 수 없으므로 불안한 상태라고 생각합니다.


commentlikes 테이블에 board_id컬럼이 있으면 바로 가져올 수 있지 않나 싶지만, 혹시 지금 모델링 구조를 바꾸지 않고 가능한 방법이 있을지 궁금해서 질문드립니다.
가능한 방법이 있다면 추천 부탁드립니다…!

자고 일어나니 사람들의 댓글이 달려있었고, 그 중 이전 기수 분께서 써준 댓글은..!!

from django.db.models import Count

queryset = comment_likes.objects.select_related('comments').filter(comments__board_id=3, is_like=1)
.values('comment_id').annotate(count=Count('is_like')).values('comment_id', 'count')

result = max(list(queryset), key=lambda x : x['count'])
'좋아요가 가장 많은 comment의 id' = result['comment_id']

친절하게 코드까지 대략 작성해서 주셨다.


그리고 이건 우리 백엔드의 빛, 멘토 소헌님께서 적어주신 정성스러운 댓글!

코드를 못짜는게 문제가 아니라, 이 상황을 벗어나기 위해 어떻게 구글링을 해야할지도 감이 오지 않을 때..!!!
정말 떠먹여주신다 할 정도로 정리해주셨다. (감사해요오.... 🥺 🥺)

그런데 정말 이 글을 보면서 갑자기 생각이 정리가 되기 시작했다.
암만 생각해도 감이 오질 않았는데,

select sum(is_like) as is_like from commentlikes 
where comment_id in (
   select id from comments 
   where board_id = 3
   ) 
order by is_like;

요로케 하면 구할 수 있다!!!!!! 이 결과물들 중 index가 0 인 것 (sum이 가장 큰 것!)을 구하면 되기 때문!!!
이제 이걸 코드로.. 잘 구현해보자..
화이팅...

(+ 구현한 코드!)
하지만 orm 자체의 메서드나 python의 list comprehension과 같은 것들을 사용하지 않은 상태.
좀 더 예쁘게 다듬어보자

class BoardListView(View):
    def get(self, request):
        try:
            board_data  = Board.objects.all()
            boards       = []
            # 전체 게시물 개수만큼 반복
            for i in board_data:
                board_id    = i.id
                # 가장 최근의 댓글 목록[0] 가져오기
                comments      = []
                ## 1. 해당 게시물의 댓글 id 추출
                comment_list = Comment.objects.filter(
                                    board_id = board_id
                                )
                comment_ids = []
                for j in range(len(comment_list)):
                    comment_ids.append(comment_list[j].id)
                ## 2. 각 댓글의 좋아요 수 sum list 중 가장 큰 값 추출 
                comment_sums = {} 
                for j in range(len(comment_ids)):
                    comment_sums[comment_ids[j]] = CommentLike.objects.filter(comment_id =comment_ids[j]).count()
                # 3. 댓글이 아예 없는 경우 대비하기
                if comment_ids:
                    best_comment_id = max(comment_sums, key = comment_sums.get)
                    comment_data = comment_list.filter(id = best_comment_id)
                    # 댓글이 있을 경우 board 리스트에 담을 준비
                    if comment_data:
                        member_id = comment_data[0].writer_id
                        comments.append({
                            'comment_num'   : len(comment_data),
                            'writer'        : Member.objects.filter(id = comment_data[0].writer_id)[0].nickname,
                            'content'       : comment_data[0].content
                        })
                # board image 준비
                board_images        = []
                board_image_data    = BoardImage.objects.filter(board_id = board_id)
                for k in range(len(board_image_data)):
                    board_images.append(
                        board_image_data[k].image_url
                    )
                # 게시물 좋아요 수 가져오기
                board_likes = BoardLike.objects.filter(board_id = board_id)
                
                # '오늘' 탭 목록을 위한 board 리스트 준비
                boards.append({
                    'board_id'      : i.id,
                    'uploader'      : i.uploader,
                    'theme'         : i.theme,
                    'title'         : i.title,
                    'content'       : i.content,
                    'created_at'    : i.created_at,
                    'comment'       : comments,
                    'board_image'   : board_images,
                    'board_likes'   : len(board_likes)
                })
            return JsonResponse({'message' : 'SUCCESS', 'board_list' : boards}, status = 200)

        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status = 400)
        except ValueError:
            return JsonResponse({'message' : 'DECODE_ERROR'}, status = 400)
        except Board.DoesNotExist:
            return JsonResponse({'message' : 'NO_EXIST_DATA'}, status = 500)
        except Board.MultipleObjectsReturned:
            return JsonResponse({'message' : 'TOO_MANY_DATA'}, status = 500)


이건 오늘 발견한 댓글 수정, 삭제 기능!

우리 팀원이 카카오측에서 테스트 중인가보다고 드립을 쳤는데....
사실 내가 달아놓은 댓글이에요....'테스트입니다'..

카카오프렌즈샵 개발팀 '오늘'탭 댓글 개발자들 사이에 혼란을 불러일으킬 존재가 될 수 있었으면 좋겠는데.. 그러진 않았겠지...👩‍💻

profile
웹 개발 🐷😎👊🏻🔥

0개의 댓글