Django 커스텀 페이지네이션 구현하기
Django 기본 Paginator, 커스텀 페이지네이션
2025년 1월 12일·4 min read·
backend
django
python
pagination
drf
api
Django 기본 Paginator 이해
Django에서 가장 간단하게 페이지네이션을 적용하려면 Paginator 클래스를 사용합니다.
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
from .models import Post
def post_list_view(request):
post_qs = Post.objects.all().order_by('-created_at')
# Paginator 객체 생성 (페이지당 10개)
paginator = Paginator(post_qs, 10)
page = request.GET.get('page', 1)
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request, 'blog/post_list.html', {'posts': posts})
핵심 개념
Paginator(queryset, per_page): queryset과 페이지당 개수를 인자로 받음paginator.page(number): 해당 페이지의Page객체 반환- 예외 처리:
PageNotAnInteger,EmptyPage예외를 적절히 처리
템플릿 예시
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
{% endfor %}
<div class="pagination">
{% if posts.has_previous %}
<a href="?page={{ posts.previous_page_number }}">이전</a>
{% endif %}
<span>{{ posts.number }} / {{ posts.paginator.num_pages }}</span>
{% if posts.has_next %}
<a href="?page={{ posts.next_page_number }}">다음</a>
{% endif %}
</div>
커스텀 Paginator가 필요한 상황
기본 Paginator로 대부분 처리할 수 있지만, 다음 경우에는 커스텀이 필요합니다:
- 페이지 번호 대신 다른 식별자 사용 (해시값, 날짜, 슬러그 등)
- Infinite Scroll 구현 시 동적 데이터 로딩
paginate_by가 동적으로 변경되어야 할 때- 복잡한 필터/검색/정렬 조건과 함께 사용
Paginator 상속으로 커스텀하기
created_at 기준 페이지네이션
from django.core.paginator import Paginator
class CreatedAtPaginator(Paginator):
"""created_at 기준 커스텀 Paginator"""
def __init__(self, object_list, per_page, **kwargs):
super().__init__(object_list, per_page, **kwargs)
def page_by_timestamp(self, timestamp):
"""timestamp 이후 데이터를 per_page만큼 반환"""
if timestamp:
filtered_qs = self.object_list.filter(created_at__lt=timestamp)
else:
filtered_qs = self.object_list
return filtered_qs.order_by('-created_at')[:self.per_page]
뷰에서 사용
def post_list_by_timestamp(request):
timestamp = request.GET.get('timestamp', None)
post_qs = Post.objects.all()
paginator = CreatedAtPaginator(post_qs, 10)
posts = paginator.page_by_timestamp(timestamp)
next_timestamp = posts.last().created_at.isoformat() if posts.exists() else None
return render(request, 'blog/post_list.html', {
'posts': posts,
'next_timestamp': next_timestamp,
})
Django Rest Framework 페이지네이션
DRF에서는 클래스 기반 Pagination을 제공합니다:
PageNumberPagination: 페이지 번호 기반LimitOffsetPagination: limit, offset 파라미터 사용CursorPagination: 커서 기반 (보안성 높음)
커스텀 PageNumberPagination
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size' # ?page_size=20 가능
max_page_size = 100
설정 방법
# settings.py - 전역 설정
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'myapp.paginations.CustomPageNumberPagination',
'PAGE_SIZE': 10,
}
# 또는 개별 ViewSet에서
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = CustomPageNumberPagination
정리
| 방식 | 사용 사례 |
|---|---|
| 기본 Paginator | 단순 페이지 번호 기반 |
| 커스텀 Paginator | 시간/슬러그 기반, 특수 로직 |
| DRF Pagination | API 응답, 동적 page_size |
프로젝트 요구사항에 따라 적절한 방식을 선택하면 됩니다.