Django

Chapter 6-5 댓글 페이지네이션 구현하기 (ListView + Bootstrap)

Chansman 2025. 5. 9. 10:08

📄 Chapter 06. 댓글 페이지네이션 구현하기 (ListView + Bootstrap)


🎯 목표

블로그 상세 페이지에서 댓글이 너무 많아지면 가독성이 떨어지므로, 댓글에 페이지네이션을 적용합니다. 이를 통해 UX를 개선하고, 서버 부하도 줄입니다.


1️⃣ 테스트용 댓글 더미 생성 (shell_plus)

python manage.py shell_plus

comment_list = Comment.objects.filter(blog_id=1)
new_list = []

for i in range(20):
    for comment in comment_list:
        comment.id = None
        new_list.append(comment)

Comment.objects.bulk_create(new_list)
exit

📌 하나의 블로그 글에 많은 댓글을 복제하여 페이지네이션 테스트 준비 완료


2️⃣ BlogDetailView를 ListView로 변경 (cb_views.py)

class BlogDetailView(ListView):
    model = Comment
    template_name = 'blog_detail.html'
    paginate_by = 10

    def get(self, request, *args, **kwargs):
        self.object = get_object_or_404(Blog, pk=kwargs.get('blog_pk'))
        return super().get(request, *args, **kwargs)

    def get_queryset(self):
        return Comment.objects.filter(blog=self.object).prefetch_related('author')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['comment_form'] = CommentForm()
        context['blog'] = self.object
        return context

✅ 댓글이 페이징된 리스트로 전달됨 (object_list)
✅ 블로그 정보는 self.object로 유지


3️⃣ Blog 모델의 get_absolute_url 수정

class Blog(TimestampModel):
    ...
    def get_absolute_url(self):
        return reverse('blog:detail', kwargs={'blog_pk': self.pk})

4️⃣ pagination.html 템플릿 추가

  • 공통 페이지네이션 UI 컴포넌트로 사용
  • fragment를 이용해 댓글 영역으로 스크롤 이동도 가능
<div class="d-flex justify-content-center">
  <ul class="pagination">
    {% if page_obj.has_previous %}
      <li class="page-item">
        <a class="page-link" href="?page=1{% if fragment %}#{{ fragment }}{% endif %}">&laquo; 첫번째</a>
      </li>
      <li class="page-item">
        <a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if fragment %}#{{ fragment }}{% endif %}">이전</a>
      </li>
    {% endif %}

    {% for i in page_obj.paginator.page_range %}
      {% if page_obj.number == i %}
        <li class="page-item"><a class="page-link active" href="#">{{ i }}</a></li>
      {% elif i > page_obj.number|add:-3 and i < page_obj.number|add:3 %}
        <li class="page-item"><a class="page-link" href="?page={{ i }}{% if fragment %}#{{ fragment }}{% endif %}">{{ i }}</a></li>
      {% endif %}
    {% endfor %}

    {% if page_obj.has_next %}
      <li class="page-item">
        <a class="page-link" href="?page={{ page_obj.next_page_number }}{% if fragment %}#{{ fragment }}{% endif %}">다음</a>
      </li>
      <li class="page-item">
        <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if fragment %}#{{ fragment }}{% endif %}">마지막 &raquo;</a>
      </li>
    {% endif %}
  </ul>
</div>

5️⃣ blog_detail.html에 페이지네이션 적용

{% if request.user.is_authenticated %}
<form method="post" action="{% url 'blog:comment_create' blog.pk %}">
  {% csrf_token %}
  {{ comment_form.as_p }}
  <div class="text-end">
    <button class="btn btn-primary">작성</button>
  </div>
</form>
{% endif %}

<div class="mb-2" id="comment_wrapper">
  {% for comment in object_list %}
    <div class="border-bottom">
      {{ comment.content }}
      <p class="text-end">
        <small>{{ comment.created_at|date:'Y-m-d H:i' }} | {{ comment.author.username }}</small>
      </p>
    </div>
  {% endfor %}
</div>

{% include 'pagination.html' with fragment='comment_wrapper' %}

6️⃣ URL 패턴 변경 (urls.py)

path('<int:blog_pk>/', cb_views.BlogDetailView.as_view(), name='detail'),

✅ 최종 요약

  • 댓글을 ListView 기반으로 페이지네이션 처리
  • fragment를 이용해 페이지 이동 후 댓글 영역으로 자동 스크롤 이동
  • 템플릿 모듈화(pagination.html)로 재사용성 향상

👉 실무에서는 Ajax로 댓글만 비동기 로딩하거나, 무한스크롤로 확장할 수 있습니다!