dboard - 댓글 보여주기 최적화 + 댓글 페이징 전처리
요 블로그에서 대강 만들어뒀던 댓글 코드를 dboard 에도 그대로 가져가서 붙여둔 상태였다. silk 로 쿼리 얼마나 많이 하나 보면서 유독 post 화면이 쿼리가 많았던 이유가 바로 댓글 때문이었다.
댓글-대댓글-대대댓글-대대대댓글 이렇게 depth 를 가질 수 있고, 설정으로 최대 깊이를 두긴 했지만 이론상 깊이 제한이 없다. 고개를 왼쪽으로 까딱 뉘여서 보면 학교에서 배우던 tree 구조라는 것을 알 수 있다. 그래서 아주 나이브하게 DFS 트리 순회를 하면서 댓글을 보여주도록 했는데, 그래서 리스트를 쭉 이터레이션하는게 아니라서 쿼리가 댓글 개수에 비례해서 늘어나는 것이다. 댓글 개수를 N 이라고 하면 O(N) 정도인 것 같다. 자세히 생각하진 않았지만 대충 그런 느낌...
https://www.sqlteam.com/articles/sql-for-threaded-discussion-forums
요걸 참고해서(이건 django-comments-xtd 소스코드 뒤지다가 걔들이 주석으로 붙여둔 링크 발견한 것) sort_order 라는걸 넣어 보았다. 여기의 sort_order 란 한 스레드 안에서 댓글이 보여지는 순서를 미리 계산해둔 것이다. precomputed field 라고 보면 된다. 한 스레드를 여러가지로 정의할 수 있는데, 보통은 포스팅을 rootnode 로 하는 스레드를 만드는 구조를 잡는게 자연스러울 것 같다... 지만 나는 한 글에 달린 1차 댓글들이 rootnode 가 되도록 했다. 이건 뭐 필요에 따라서 적당히 하면 된다. 최신댓글을 위에 보여줄거냐 밑에 보여줄거냐 등등은 sort_order 를 계산에 내 의도에 맞게 반영하면 된다.
views.py 에서는 comment.parent 세팅 이외의 다른 조작은 하지 않고, 나머지 thread, timestamps, depth_level, sort_order 등은 모두 signals.py 에서 계산하도록 해 두었다. 그리하여, @receiver(pre_save, sender=Comment) 시그널 받아 처리하는 함수(댓글 객체가 저장되기 전에 호출되는 hook) 아래에 아래 코드를 추가.
# sort_order를 계산해보자.if instance.parent is None:instance.sort_order = 0else:queryset = Comment.objects.filter(thread=instance.thread,post_level__lte=instance.parent.post_level,sort_order__gt=instance.parent.sort_order)if queryset.exists():min_sort_order = queryset.aggregate(Min('sort_order'))['sort_order__min']print(f'min_sort_order: {min_sort_order}')Comment.objects.filter(thread=instance.thread,sort_order__gte=min_sort_order).update(sort_order=F('sort_order') + 1)instance.sort_order = min_sort_orderelse:max_sort_order = Comment.objects.filter(thread=instance.thread) \.aggregate(Max('sort_order'))['sort_order__max'] or 0print(f'max_sort_order: {max_sort_order}')instance.sort_order = max_sort_order + 1