thumbnail
pagination(페이지네이션) 추가하기
Jan 12, 2023

gatsby 블로그에 페이지네이션을 추가해보자.

Pagination

Pagination(페이지네이션)은 콘텐츠를 여러 페이지에 나누어 보여주고 이전 페이지나 다음 페이지로의 이동 버튼을 제공하는 기술이다. 콘텐츠의 양이 많아질 때 모든 데이터를 한 번에 렌더링하면 웹 페이지 속도가 느려질 수 있는데, 페이지네이션을 사용하면 이러한 속도 저하를 개선할 수 있다.


내 블로그의 경우 카테고리별로 페이지가 존재하기 때문에 전체 콘텐츠가 아닌, 각각의 페이지별로 페이지네이션 처리를 해줄 것이다.

check list

한 페이지에 보여줄 콘텐츠의 개수

카테고리별 페이지네이션 구현

이전 페이지, 다음 페이지로 이동하는 버튼


페이지별 콘텐츠 수

페이지를 이동하면 이전 페이지의 마지막 콘텐츠에 이어 다음 콘텐츠들을 보여줄 수 있어야 한다. 이때 사용하기 좋은 GraphQL의 인수로 limitskip이 있다.

limit

limit는 처음을 기준으로 지정한 수만큼만 데이터를 표시한다. 콘텐츠가 총 10개인데 limit가 5라면 처음 콘텐츠를 기준으로 5개의 글만 보이게 된다.

{ allMarkdownRemark(limit: 5) { totalCount edges { node { id } } } }

쿼리로 확인해보면 전체 글 개수(=totalCount)는 42이지만 limit를 5로 설정했을 때 결과로 5개의 글 제목만 표시된다.

GraphQL limit argument

skip

skip은 지정한 숫자만큼 결과를 생략하고 건너뛴다. 예를 들어 skip을 3으로 설정한다면 처음 콘텐츠로부터 3개를 건너뛴 4번째 결과부터 표시된다.

{ allMarkdownRemark(skip: 3) { edges { node { frontmatter { title } } } } }

쿼리 결과 skip을 지정하면 지정된 수만큼 생략된 결과가 나타나는 것을 확인할 수 있다.

GraphQL skip argument GraphQL skip argument

페이지 생성

한 페이지당 보여줄 콘텐츠 수를 정하고 이에 따라 다수의 페이지를 생성해 줄 차례이다. gatsby-node.js 파일에 다음과 같이 작성한다.


먼저, postsPerPage에 페이지 당 보여줄 글 개수를 지정한다. 페이지 수는 전체 글 개수를 페이지 당 글 개수로 나눈 몫을 반올림하여 numPages에 저장한다.

/* gatsby-node.js */ const queryAllMarkdownData = await graphql( ` { // ... categoriesGroup: allMarkdownRemark(limit: 2000) { group(field: frontmatter___categories) { fieldValue nodes { id } } } } `, ); // ... // 카테고리별로 묶은 배열 const categories = queryAllMarkdownData.data.categoriesGroup.group; categories.forEach((category) => { const postsPerPage = 6; // 페이지 당 보여줄 콘텐츠 개수 let numPages = Math.ceil(category.nodes.length / postsPerPage); // 페이지 수 });

다음으로 for 문을 이용해 numPages만큼 페이지를 생성해준다. 처음 페이지일 때의 경로는 categories/algorithm/이고 그 이외에는 categories/algorithm/2, categories/algorithm/3 등 첫 경로 뒤에 숫자가 1씩 증가하는 순으로 페이지를 생성한다.


그리고 contextlimitskip을 아래와 같이 설정한다. limit는 페이지 당 보여줄 글 개수이고 skip은 현재 페이지 인덱스의 postsPerPage배만큼 건너뛰어야 한다.

이전 페이지, 다음 페이지로의 이동을 위해 numPagescurrentPage도 추가한다.

const categoryTemplate = path.resolve('src/templates/blog_template.tsx'); const categories = queryAllMarkdownData.data.categoriesGroup.group; categories.forEach((category) => { const postsPerPage = 5; let numPages = Math.ceil(category.nodes.length / postsPerPage); for (let i = 0; i < numPages; i++) { createPage({ path: i === 0 ? `/categories/${_.kebabCase(category.fieldValue)}` : `/categories/${_.kebabCase(category.fieldValue)}/${i + 1}`, component: categoryTemplate, context: { category: category.fieldValue, image: _.kebabCase(category.fieldValue), limit: postsPerPage, skip: i * postsPerPage, totalCount: category.nodes.length, numPages, currentPage: i + 1, }, }); } });

component로 설정한 파일의 쿼리에는 limitskip을 추가한다.

export const blogQuery = graphql` query Category($category: String $image: String $skip: Int $limit: Int) { allMarkdownRemark( sort: { fields: [frontmatter___date], order: DESC } filter: { frontmatter: { categories: { in: [$category] } } } limit: $limit skip: $skip ) { // ... } } `;

페이지 이동

버튼 구현

이제 실제 페이지에 페이지네이션을 적용하자. pageContext로 필요한 데이터를 불러와 주고 아래와 같이 작성한다.

isFirstisLast는 각각 첫 번째 페이지인지 마지막 페이지인지 확인하는 boolean 값이다. prevPagenextPage는 이전 페이지, 다음 페이지에 해당하는 경로이다.

const BlogTemplate: FunctionComponent<BlogTemplateProps> = ({ // ... pageContext: { category, totalCount, numPages, currentPage }, }) => { const isFirst = currentPage === 1; const isLast = currentPage === numPages; const prevPage = currentPage - 1 === 1 ? `/categories/${kebabCase(category)}/` : `/categories/${kebabCase(category)}/${currentPage - 1}`; const nextPage = `/categories/${kebabCase(category)}/${currentPage + 1}`; // ... };

prevPagenextPage로의 이동을 구현해주고 처음 페이지일 때 이전 페이지 버튼이, 마지막 페이지일 때 다음 페이지 버튼이 아예 표시되지 않으면 정렬이 흐트러지므로 visibility 속성을 추가해 주었다. (자리는 차지하되, 보이지 않게)

const BlogTemplate: FunctionComponent<BlogTemplateProps> = ( { // ... }, ) => { // ... <PaginationWrapper> <Link to={prevPage} rel="prev" style={{ visibility: `${isFirst ? 'hidden' : 'visible'}` }} > <FontAwesomeIcon icon={faArrowLeft} /> Prev Page </Link> <Link to={nextPage} rel="next" style={{ visibility: `${isLast ? 'hidden' : 'visible'}` }} > Next Page <FontAwesomeIcon icon={faArrowRight} /> </Link> </PaginationWrapper>; }; Pagination

페이지 목록

페이지 목록은 numPages 길이만큼 배열을 만들고 인덱스를 순회하며 해당 페이지로의 이동이 가능한 링크를 나열한다. 현재 위치한 페이지는 activeStyle 속성을 이용해 표현한다.

{ Array.from({ length: numPages }, (_, i) => ( <Link key={`pageNum${i + 1}`} to={`/categories/${kebabCase(category)}/${i === 0 ? `` : i + 1}`} activeStyle={{ background: `var(--point-bg)` }} > {i + 1} </Link> )); }

이렇게 카테고리별 페이지 하단에 페이지네이션이 추가되었다.

Pagination

아직은 글이 많이 없어서 간단하게 구현했지만, 나중에 페이지 개수가 많아졌을 때 좀 더 디테일하게 보완할 예정이다. 페이지 목록을 몇 개 띄워줄 건지, 제일 첫 번째 페이지 마지막 페이지로의 한 번에 이동과 같은,,


References

https://www.gatsbyjs.com/docs/adding-pagination/

Table Of Contents
nxnaxx blog © 2022-2024 Powered By Gatsby.