[React] 페이지네이션(pagination) 페이징 처리

이해용·2022년 7월 28일
7
post-thumbnail

기업 협업 중 페이지네이션을 해야하는 상황이 생겼다. 라이브러리를 사용하면 쉽다고 했으나 페이지네이션에 대한 이해를 하고 싶어 라이브러리 없이 사용하는 방법에 대해 많은 검색을 했다. 페이지 네이션은 앞으로도 어디서든 많이 사용할 것이라는 생각이 들어 정리를 하려 한다.

pagination(페이지네이션)?

페이징(paging)이라고도 하는 페이지네이션(pagination)은 문서를 전자 페이지 또는 인쇄된 페이지와 같은 개별 페이지로 나누는 프로세스입니다.

예로는 아래 사진과 같다.

페이지네이션

적용 방법

처음 Daleseo님의 블로그를 따라할 때는 잘 적용되었으나 데이터의 개수가 많아 페이지가 많아지면 많아지는 만큼 페이지네이션의 개수도 무한히 증가한다. 이 부분에 대해 처음에는 검색을 어떻게 해야할지 몰라 많은 시간을 헤매게 되었고 페이징 처리라는 단어를 나중에 알게 되어 리액트로 페이징 처리를 한 블로그 를 찾을 수 있었다. 두 개의 블로그를 참고하니 내가 원하는 대로 페이지네이션을 처리할 수 있었다.

코드

※ 작성한 코드는 next.js 와 TypeScript를 사용하여 진행했습니다.

//list.tsx

const List = (): JSX.Element => {
  
  const [lists, setLists] = useState([] as any | undefined); // 백엔드와 통신하여 모든 데이터를 setLists 에 저장해서 사용
  const [limit, setLimit] = useState(10); // 한 페이지에 보여줄 데이터의 개수
  const [page, setPage] = useState(1); // 페이지 초기 값은 1페이지
  const [counts, setCounts] = useState(1); // 데이터의 총 개수를 setCounts 에 저장해서 사용
  const [blockNum, setBlockNum] = useState(0); // 한 페이지에 보여 줄 페이지네이션의 개수를 block으로 지정하는 state. 초기 값은 0
  
...
return (
...  
      <ListPagination
        limit={limit}
        page={page}
        setPage={setPage}
        blockNum={blockNum}
        setBlockNum={setBlockNum}
        counts={counts}
      />
...


//ListPagination.tsx

const ListPagination = ({
  limit,
  page,
  setPage,
  blockNum,
  setBlockNum,
  counts,
}: {
  limit: number;
  page: number;
  setPage: Function;
  blockNum: number;
  setBlockNum: Function;
  counts: number;
}): JSX.Element => {
const createArr = (n: number) => {
    const iArr: number[] = new Array(n);
    for (let i = 0; i < n; i++) iArr[i] = i + 1;
    return iArr;
  }; // 새로운 배열을 만들기 위한 함수

  const pageLimit = 10; // 보여줄 페이지네이션 개수

  const totalPage: number = Math.ceil(counts / limit); //총 데이터의 개수(counts)를 한 페이지의 보여줄 데이터(limit)로 나눠 올림을 하면 전체 페이지의 개수가 나온다.

  const blockArea: number = Number(blockNum * pageLimit); // 화면 전환 할 때 보여줄 페이지네이션 개수를 구역으로 지정한다.
  const nArr = createArr(Number(totalPage)); // nArr 함수에 전체 페이지의 개수를 배열로 담는다.
  let pArr = nArr?.slice(blockArea, Number(pageLimit) + blockArea); // 페이지네이션 구역을 nArr 함수에 slice하여 원하는 페이지네이션 block 만 보여 줄 수 있게 설정

  const firstPage = () => {
    setPage(1);
    setBlockNum(0);
  };

  const lastPage = () => {
    setPage(totalPage);
    setBlockNum(Math.ceil(totalPage / pageLimit) - 1);
  };

  const prevPage = () => {
    if (page <= 1) { 
      return; 
    } // page가 1보다 작거나 같으면 아무 것도 리턴하지 않는다.
    if (page - 1 <= pageLimit * blockNum) {
      setBlockNum((n: number) => n - 1);
    } // 현재 페이지 - 1 이 보여줄 페이지네이션 개수(pageLimit) * blockNum 보다 작거나 같으면 setBlockNum에 - 1 을 작동시킨다.
    setPage((n: number) => n - 1); // setPage를 현재 페이지에서 -1 로 이동시킨다.
  };

  const nextPage = () => {
    if (page >= totalPage) {
      return;
    } // page가 마지막 페이지보다 크거나 같으면 아무 것도 리턴하지 않는다.
    if (pageLimit * Number(blockNum + 1) < Number(page + 1)) {
      setBlockNum((n: number) => n + 1);
    } //보여줄 페이지네이션 개수(pageLimit) * (blockNum+1) 가 page + 1보다 작다면 setBlockNum은 현재 페이지 + 1을 한다.
    setPage((n: number) => n + 1); //setPage에 현재 페이지 + 1을 한다.
  };
  
  return (
    <div className="ListPagenationWrapper">
      <button
        className="moveToFirstPage"
        onClick={() => {
          firstPage();
        }}
      >
        &lt;&lt;
      </button>
      <button
        className="moveToPreviousPage"
        onClick={() => {
          prevPage();
        }}
        disabled={page === 1}
      >
        &lt;
      </button>
      <div className="pageBtnWrapper">
        {pArr.map((n: number) => (
          <button
            className="pageBtn"
            key={n}
            onClick={() => {
              setPage(n);
            }}
            aria-current={page === n ? 'page' : undefined}
          >
            {n}
          </button>
        ))}
      </div>
      <button
        className="moveToNextPage"
        onClick={() => {
          nextPage();
        }}
        disabled={page === totalPage}
      >
        &gt;
      </button>
      <button
        className="moveToLastPage"
        onClick={() => {
          lastPage();
        }}
      >
        &gt;&gt;
      </button>
    
      <style jsx>
        {`
          .ListPagenationWrapper {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 37px;
            margin: 38px 94px 38px 88px;
			}

            .moveToPreviousPage,
            .moveToNextPage {
              color: #5a5a5a;
              background-color: transparent;
              border: none;
              font-size: 25px;
              cursor: pointer;
            }

            .moveToFirstPage,
            .moveToLastPage {
              width: 115px;
              height: 37px;
              margin: 0 0 0 0;
              border: none;
              color: black;
              background-color: transparent;
              cursor: pointer;
            }

            .pageBtn {
              width: 49px;
              height: 49px;
              margin: 0 10px;
              border: none;
              color: black;
              font-size: 20px;
              opacity: 0.2;

              &:hover {
                background-color: #b42954;
                cursor: pointer;
                transform: translateY(-2px);
              }

              &[disbled] {
                background-color: #e2e2e2;
                cursor: revert;
                transform: revert;
              }

              &[aria-current] {
                background-color: #f5d3dd;
                font-weight: bold;
                cursor: revert;
                transform: revert;
                opacity: 1;
              }
            }          
        `}
      </style>
    </div>
  );
};

export default ListPagenation;

결과 화면

페이지네이션 결과 화면

reference
https://en.wikipedia.org/wiki/Pagination
https://www.daleseo.com/react-pagination/
https://blog.naver.com/PostView.nhn?blogId=senshig&logNo=222026171254&parentCategoryNo=&categoryNo=85&viewDate=&isShowPopularPosts=false&from=postView

profile
프론트엔드 개발자입니다.

0개의 댓글