언어/Next.js

[Next 고캠핑] 페이지네이션 커스텀하기

홍시_코딩기록 2024. 7. 24. 23:45

 

페이지네이션을 만드려고 찾아보다가 
react-paginate 라이브러리를 선택해서 만들었다.

하지만,, 마음에 안든다..

가운데 ...도 거슬리고 페이지가 5개씩만 나오게 하고 싶은데 찾아보니 안된다고 한다. 

라이브러리 선택하는 것도 잘 해야한다더니 그 말이 맞음,,

 

다시 페이지네이션을 만들어보기로 한다..

변경한 페이지네이션

 


export default function Pagination({
  totalItems,
  itemCountPerPage,
  pageCount,
  onClick,
  currentPage,
}: IPropsPage) {
  const totalPages = Math.ceil(totalItems / itemCountPerPage); // 총 페이지 개수
  const [start, setStart] = useState<number>(1); // 시작 페이지
  
      {[...Array(pageCount)].map((_, i) => {
        const pageNumber = start + i;
        return (
          pageNumber <= totalPages && (
            <li
              className={`${currentPage === pageNumber ? "select" : ""}`}
              onClick={() => {
                onClick(pageNumber);
              }}
              key={pageNumber}
            >
              {pageNumber}
            </li>
          )
        );
      })}

페이지는 pageCount만큼 나오게 한다 (내가 5로 지정해 둠)

페이지 숫자는 총 페이지 개수까지만 나오고

현재 페이지가 페이지 숫자와 같을 때 클래스를 추가해줬다. (현재 페이지 스타일 주기 위함)

onClick으로 페이지 이동

 

 


  const noPrev = currentPage === 1; // 이전 페이지가 없는 경우
  const noNext = currentPage === totalPages; // 다음 페이지가 없는 경우

  useEffect(() => {
    if (currentPage >= start + pageCount) { // 페이지 카운트 다음 페이지로 갈떄
      setStart((prev) =>
        Math.min(prev + pageCount, totalPages - pageCount + 1),
      );
    } else if (currentPage < start) { // 페이지 카운트 이전 페이지로 갈 떄
      setStart((prev) => Math.max(prev - pageCount, 1));
    }
  }, [currentPage, pageCount, totalPages, start]);

  const handlePrevClick = () => {
    if (!noPrev) {
      onClick(currentPage - 1);
    }
  };

  const handleNextClick = () => {
    if (!noNext) {
      onClick(currentPage + 1);
    }
  };
  
  
      <li
        onClick={handlePrevClick}
        className={`prev ${noPrev ? "disabled" : ""}`}
      >
        <FiChevronLeft style={{ verticalAlign: "text-top" }} />
      </li>
      
      
      <li
        onClick={handleNextClick}
        className={`next ${noNext ? "disabled" : ""}`}
      >
        <FiChevronRight style={{ verticalAlign: "text-top" }} />
      </li>

다음 페이지 카운트로 넘어갈 때 (currentPage >= start + pageCount)

그 단락의 첫 숫자를 알아야 하니까 prev + pageCount를 해준다.

하지만 마지막 단락일경우

예를 들면 마지막 페이지(totalPages)= 8/ pageCount = 5이라 가정한다.

prev + pageCount는 6이지만 5개로 맞춰서 보여주기 위해

totalPages - pageCount + 1을 해준다 (8 - 5 + 1 = 4)

그래서 마지막 단락으로 오면 4, 5, 6, 7, 8 이 나오게 된다!

 

이전 페이지로 넘어갈 때 (currentPage < start)

prev 에서 pageCount만큼 뺀 숫자 (이전 페이지의 첫 숫자)로 돌아가지만

1페이지에서 1보다 작은 값을 가지면 안되니까 

Math.max(prev - pageCount, 1) 1을 넣어줬다.

 

 

사용 방법

const PER_PAGE = 8; // 한 페이지에 보여줄 아이템 수

export default function CampingCard({ className }: IPropsList) {
  const [list, setList] = useState<ICampingList[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [currentPage, setCurrentPage] = useState<number>(1); // 현재 페이지 번호
  const [totalCount, setTotalCount] = useState<number>(0); // 전체 캠핑 아이템 수
  
  ...
  
  const pageCount = Math.ceil(totalCount / PER_PAGE); // 전체 페이지 수 계산
  const onClickPage = (selected: number) => {
    setCurrentPage(selected);
  };
  ...
  
  
      {/* 페이지네이션 */}
      {pageCount > 0 && (
        <Pagination
          totalItems={totalCount}
          onClick={onClickPage}
          currentPage={currentPage}
          pageCount={5}
          itemCountPerPage={PER_PAGE}
        />
      )}

전체 코드

더보기
import styled from "@emotion/styled";
import { useState, useEffect } from "react";
import { FiChevronLeft, FiChevronRight } from "react-icons/fi";

const PageWrap = styled.ul`
  display: flex;
  justify-content: center;
  text-align: center;

  li {
    line-height: 2.8rem;
    min-width: 2.8rem;
    height: 2.8rem;
    cursor: pointer;

    &.disabled svg {
      stroke: #ccc;
    }
    &.select {
      background: #67794a;
      color: #fff;
      border-radius: 50%;
    }
  }
`;
interface IPropsPage {
  totalItems: number;
  itemCountPerPage: number;
  pageCount: number;
  currentPage: number;
  onClick: (selected: number) => void;
}

export default function Pagination({
  totalItems,
  itemCountPerPage,
  pageCount,
  onClick,
  currentPage,
}: IPropsPage) {
  const totalPages = Math.ceil(totalItems / itemCountPerPage); // 총 페이지 개수
  const [start, setStart] = useState<number>(1); // 시작 페이지
  const noPrev = currentPage === 1; // 이전 페이지가 없는 경우
  const noNext = currentPage === totalPages; // 다음 페이지가 없는 경우

  useEffect(() => {
    if (currentPage >= start + pageCount) { // 페이지 카운트 다음 페이지로 갈떄
      setStart((prev) =>
        Math.min(prev + pageCount, totalPages - pageCount + 1),
      );
    } else if (currentPage < start) { // 페이지 카운트 이전 페이지로 갈 떄
      setStart((prev) => Math.max(prev - pageCount, 1));
    }
  }, [currentPage, pageCount, totalPages, start]);

  const handlePrevClick = () => {
    if (!noPrev) {
      onClick(currentPage - 1);
    }
  };

  const handleNextClick = () => {
    if (!noNext) {
      onClick(currentPage + 1);
    }
  };
  return (
    <PageWrap>
      <li
        onClick={handlePrevClick}
        className={`prev ${noPrev ? "disabled" : ""}`}
      >
        <FiChevronLeft style={{ verticalAlign: "text-top" }} />
      </li>
      {[...Array(pageCount)].map((_, i) => {
        const pageNumber = start + i;
        return (
          pageNumber <= totalPages && (
            <li
              className={`${currentPage === pageNumber ? "select" : ""}`}
              onClick={() => {
                onClick(pageNumber);
              }}
              key={pageNumber}
            >
              {pageNumber}
              <p>s: {start}</p>
              <p>i: {i}</p>
              <p>c: {currentPage}</p>
            </li>
          )
        );
      })}
      <li
        onClick={handleNextClick}
        className={`next ${noNext ? "disabled" : ""}`}
      >
        <FiChevronRight style={{ verticalAlign: "text-top" }} />
      </li>
    </PageWrap>
  );
}