Nextjs 페이지네이션 구현하기

Nextjs

  • pagination

2023-04-21 07:57

✍🏻 Table Of Contents

Pagination

하기 이미지처럼 데이터를 한번에 보여주는 것이 아니라 페이지 번호로 나누어서 정해진 갯수만큼 보여준다

구현

blog/index.js

const Homepage= ({ posts, numPages }) => {
  return (
    <Layout data={posts}>
      <PostList data={posts} numPages={numPages} />
    </Layout>
  );
};

export default Homepage;

export async getStaticProps() {
	const res = await fetch('https://...') // 데이터 가져오기
	const allPosts = await res.json();

	const postsPerPage = 6; // 한페이지에 몇 개의 컨텐츠를 보여줄건지 정의
	const numPages = Math.ceil(allPosts.length / postsPerPage); // 총 페이지의 갯수 

	const posts = allPosts.slice(0, postsPerPage); // blog/index.js 는 첫 페이지를 의미한다

	return {
		props: {posts, numPages},
	}
}
javascript

blog/[pageNumber].jsx

const Page = ({ posts, numPages }) => {
  return (
    <Layout data={posts}>
      <PostList data={posts} numPages={numPages} />
    </Layout>
  );
};

export default Page;


// build시 생성할 파일 정의
export async function getStaticPaths() {
  const res = await fetch('https://...') // 데이터 가져오기
  const allPosts = await res.json();

  const postsPerPage = 6; // 한페이지에 몇 개의 컨텐츠를 보여줄건지 정의
  const numPages = Math.ceil(allPosts.results.length / postsPerPage); // 총 페이지의 갯수 

  const paths = [];

  for (let i = 1; i <= numPages; i++) {
    paths.push({ params: { pageNumber: i.toString() } });
  }

  return { paths, fallback: false };
}

// 위 코드에 의해 numPages가 3이라고 하면 blog/1, blog/2, blog/3 페이지 생성

export async function getStaticProps({ params }) {

  const res = await fetch('https://...') // 데이터 가져오기
  const allPosts = await res.json();

  const postsPerPage = 6; // 한페이지에 몇 개의 컨텐츠를 보여줄건지 정의
  const numPages = Math.ceil(allPosts.results.length / postsPerPage); // 총 페이지의 갯수 

  const offset = (params.pageNumber - 1) * postsPerPage;

  const posts = allPosts.results.slice(offset, offset + postsPerPage);

  return {
    props: { posts, numPages },
  };
}
javascript

components/PostList

const PostList = ({ data, numPages }) => {
	return (
		<>
			...
			<PostPagination numPages={numPages} />
		</>
	)
}
javascript

components/PostPagination

import styled from "@emotion/styled";
import { useRouter } from "next/router";
import { useState, useEffect } from "react";

export default function PostPagination({ numPages }) {
  const router = useRouter();

  const [page, setPage] = useState(1);

  useEffect(() => {
    setPage(router.query.pageNumber === undefined ? 1 : parseInt(router.query.pageNumber));
  }, [router.query.pageNumber]);

  const handlePrevClick = () => {
    router.push(`/blog/${page - 1}`);
    setPage(page - 1);
  };

  const handleNextClick = () => {
    router.push(`/blog/${page + 1}`);
    setPage(page + 1);
  };

  return (
    <Base spacing={2}>
      <Pagination>
        <PrevBtn disabled={page === 1} onClick={handlePrevClick}>
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
            <path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
          </svg>
        </PrevBtn>
        {new Array(numPages).fill(0).map((v, i) => {
          return (
            <PaginationItem page={page === i + 1} key={i} onClick={(e) => router.push(`/blog/${e.target.innerText === "1" ? "" : e.target.innerText}`)}>
              {i + 1}
            </PaginationItem>
          );
        })}
        <NextBtn disabled={page === numPages} onClick={handleNextClick}>
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5">
            <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
          </svg>
        </NextBtn>
      </Pagination>
    </Base>
  );
}

const PaginationItem = styled.li`
  background-color: ${({ page }) => (page ? "var(--purple-color)" : undefined)};
  color: ${({ page }) => (page ? "#fff" : undefined)};
  width: 30px;
  height: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px;
  border-radius: 50%;
`;

const PrevBtn = styled.li`
  width: 25px;
  height: 25px;

  opacity: ${({ disabled }) => (disabled ? "0.2" : undefined)};
  pointer-events: ${({ disabled }) => (disabled ? "none" : undefined)};
`;
const NextBtn = styled.li`
  width: 25px;
  height: 25px;

  opacity: ${({ disabled }) => (disabled ? "0.2" : undefined)};
  pointer-events: ${({ disabled }) => (disabled ? "none" : undefined)};
`;
javascript