Django 페이징 추가하기

Kangjik Kim·2025년 1월 23일
0

쟝고에는 페이징 데이터를 쉽게 관리할 수 있는 페이징을 휘한 클래스가 내장되어 있다.
페이지당 반환할 객체 수를 정할 수 있고, 사용자가 요청한 페이지에 해당하는 게시글들을 조회할 수 있다.

게시글 목록 뷰에 페이징 추가하기

blog의 views.py 파일을 편집해 Paginator 클래스를 불러오고 아래와 같이 수정해 보자.

def post_list(request):
    post_list = Post.published.all()

    paginator = Paginator(post_list, 3)
    page_number = request.GET.get('page', 1)
    posts = paginator.page(page_number)

    return render(request, 'blog/post/list.html', {'posts': posts})
  • paginator = Paginator(post_list, 3)
    • 페이지당 반환할 객체 수와 함께 Paginator 클래스를 인스턴스화한다.
    • 페이지당 3개의 게시글을 표시한다.
  • page_number = request.GET.get('page', 1)
    • HTTP GET에서 page 파라미터를 조회해 그 값을 page_number 매개 변수에 저장하는데,
      page 매개 변수가 파라미터에 없을 경우 기본 값으로 1을 사용해 첫 페이지를 로드하게 했다.
  • posts = paginator.page(page_number)
    • Paginator의 page() 메서드를 호출해 원하는 페이지의 객체를 얻는다.
    • 이 메서드는 posts 변수에 담을 Page 객체를 반환한다.

페이징 템플릿 만들기

사용자가 다른 페이지들을 조회할 수 있도록 페이지 네비게이션을 만들어야 한다.
페이징 링크를 표시하는 템플릿을 생성하고, 웹사이트에서 다른 객체들의 페이징에서도 템플릿을 재사용할 수 있도록 일반화해 보자.

templates/ 디렉토리에 pagination.html 파일을 만들고 코드를 작성해보자.

<div class="pagination">
    <span class="step-links">
        {% if page.has_previous%}
        <a href="?page={{page.previous_page_number}}"> Previous</a>
        {% endif %}
        <span class="current">
            Page {{page.number}} of {{page.paginator.num_pages}}
        </span>
        {% if page.has_next%}
        <a href="?page={{page.next_page_number}}"> Next</a>
        {% endif %}
    </span>
</div>

이 템플릿은 페이징을 일반화한 템플릿으로,
템플릿은 이전과 다음 링크를 렌더링하고 현재 페이지와 결과의 총페이지를 표시하기 위한 Page 객체를 context에 가지고 있어야 한다.

아래와 같이 blog/post/list.html 템플릿으로 돌아가 content 블럭 하단에 pagenation.html 템플릿을 포함시키자.

{% include "pagination.html" with page=posts %}

include 템플릿 태그는 지정된 템플릿을 불러와 현재 템플릿 컨텍스트를 사용해 렌더링한다.
그리고 추가 context 변수를 전달하기 위해 with을 사용해 posts 객체를 넘겨 준다.

이제 브라우저에서 http://localhost:8000/blog 경로를 확인하면 게시글 목록 하단에 내비게이션 링크가 표시되게 된다.

Next를 클릭하면 마지막 게시글이 표시되는데, 두 번째 page의 URL에는 ?page=2 처럼 GET 매개 변수가 포함되어 있다. 이 매개변수는 뷰에서 paginator를 통해 요청된 결과 페이지를 가져오는 데 사용된다.

페이징 오류 처리하기

이제 뷰에서 페이징 오류에 대한 예외 처리를 추가해 보자.
page 파라미터는 지정된 페이지를 검색하기 위해 뷰에서 사용되는데, 존재하지 않는 페이지 번호라 페이지 번호로 쓸 수 없는 문자열과 같이 잘못된 값으로 인해 오류가 발생할 수 있다.
이런 경우 적절한 오류 처리를 구현해야 한다.

브라우저에 http://localhost:8000/blog/?page=9999를 열어보면 아래와 같은 오류 페이지가 표시될 것이다.

Paginator 객체는 9999페이지를 조회할 때 범위를 벗어날 때 EmptyPage 에러를 발생시킨다.
뷰에서 이 오류를 처리해 보자.

views.py 파일을 열어 EmptyPage를 import하고 post_list 뷰를 아래와 같이 수정해보자.

from django.core.paginator import Paginator, EmptyPage

def post_list(request):
    post_list = Post.published.all()

    paginator = Paginator(post_list, 3)
    page_number = request.GET.get('page', 1)
    try:
        posts = paginator.page(page_number)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'blog/post/list.html', {'posts': posts})

페이지를 검색할 때 예외를 처리하기 위해 try, except 블록을 추가했다.
요청한 페이지가 범위를 벗어나면 paginator.num_pages로 총 페이지수를 얻어 마지막 페이지를 반환한다.
총 페이지 수는 마지막 페이지 번호와 동일하다.
이제 뷰에서 다시 http://localhost:8000/blog/?page=9999에 접속할 경우 예외를 처리해 페이지네이션 결과의 마지막 페이지를 반환한다.

page 매개 변수에 정수가 아닌 다른 것이 전달되는 경우에도 뷰가 처리해야 한다.
브라우저에서 http://localhost:8000/blog/?page=qwer 를 접속해 보자.

이 경우 페이지 번호는 정수만 될 수 있기 때문에 PageNotAnInteger 예외가 발생하게 된다.
뷰에서 이 오류를 처리해 보자.

views.py 파일을 편집해 PageNotAnInteger 를 import하고 아래와 같이 post_list 뷰를 수정하자.

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

def post_list(request):
    post_list = Post.published.all()

    paginator = Paginator(post_list, 3)
    page_number = request.GET.get('page', 1)
    try:
        posts = paginator.page(page_number)
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'blog/post/list.html', {'posts': posts})

페이지를 조회할 때 PageNotAnInteger 에러를 처리하기 위해 새로운 except 블록을 추가했다.
요청한 페이지가 정수가 아닌 경우 결과의 첫 번째 페이지를 반환한다.

다시 http://localhost:8000/blog/?page=qwer 페이지를 방문하면,
뷰에서 예외를 처리해 결과의 첫 번째 페이지가 반환되게 된다.

0개의 댓글