React에서 페이지네이션 구현하기

페이지네이션을 쉽게 생성해주는 좋은 라이브러리도 많지만 직접 구현해 보기로 했다.

구현 기능

  • 버튼 구성: 이전으로 버튼, 페이지 숫자 버튼들, 다음으로 버튼
  • 페이지 숫자는 (현재 페이지 - 4) 부터 (현재 페이지 + 4) 까지 총 9개(최대)가 보이는 형태
  • 페이지 숫자 클릭하면 현재 페이지가 해당 페이지로 변경
  • 현재 페이지가 첫 페이지면 이전으로 버튼 비활성화
  • 현재 페이지가 마지막 페이지면 다음으로 버튼 비활성화
  • 현재 페이지가 바뀌면 URL parameter 도 바꿔주기

구현 코드

리스트 컴포넌트

  • react-router-dom 설치가 필요.
  • 현재 페이지는 useSearchParams를 이용해 받아온다. 값이 없으면 기본 1페이지이다.
  • useSearchParams 가져오는 값이 기본 문자열이기 때문에 parseInt를 사용해 정수로 바꿔줬다.
  • 페이지네이션 컴포넌트로 최대 페이지(maxPage), 현재페이지(currentPage), 클릭시 실행될 함수(onClickPageButton)을 넘긴다.
import { useEffect, useState } from "react"
import { useSearchParams } from "react-router-dom"
import ListPagination from "../components/.../ListPagination"

function ListPage() {
  // 페이지네이션 관련
  const [searchParams, setSearchParams] = useSearchParams()
  const [myListMaxPage, setMyListMaxPage] = useState(1)
  const myListPage = parseInt(searchParams.get("mylistpage") ?? "1", 10)

  function updateParams(updates) {
    setSearchParams({
      mylistpage: parseInt(searchParams.get("mylistpage") ?? "1", 10),
      ...updates,
    })
  }

  useEffect(() => {
    // 리스트를 받아온다.
    setMyListMaxPage(/* 최대 페이지 저장 */)
  }, [])

  return (
    <>
      <div>리스트 아이템들이 보이는 부분</div>
      <ListPagination
        maxPage={myListMaxPage}
        currentPage={myListPage}
        onClickPageButton={(pageNumber) =>
          updateParams({ mylistpage: pageNumber })
        }
      />
    </>
  )
}

export default ListPage

페이지네이션 컴포넌트

  • 버튼 컴포넌트(PageButton)는 반복되기 때문에 따로 빼두었지만 짧아서 파일 분리까지는 하지 않았다.
  • 숫자 버튼은 현재 페이지를 기준으로 이전 4개, 이후 4개 범위만 나타나도록 했다.
function ListPagination({ currentPage, maxPage, onClickPageButton }) {
  return (
    <div>
      {/* 이전 버튼 */}
      <button
        disabled={currentPage === 1 || maxPage === 0}
        onClick={() => {
          onClickPageButton(currentPage - 1)
        }}
      >
        이전
      </button>

      {/* 페이지 버튼 최대 9개가 보임 */}
      {new Array(9).fill(null).map((_, index) => {
        if (
          currentPage - (4 - index) >= 1 &&
          currentPage - (4 - index) <= maxPage
        ) {
          return (
            <PageButton
              key={currentPage - (4 - index)}
              number={currentPage - (4 - index)}
              onClick={onClickPageButton}
              selected={currentPage === currentPage - (4 - index)}
            />
          )
        }
      })}

      {/* 다음 버튼 */}
      <button
        disabled={currentPage === maxPage || maxPage === 0}
        onClick={() => {
          onClickPageButton(currentPage + 1)
        }}
      >
        다음
      </button>
    </div>
  )
}

// 버튼 컴포넌트
function PageButton({ number, onClick, selected }) {
  return (
    <button
      className={`${selected && "선택되었을 때 스타일 클래스"}`}
      onClick={() => onClick(number)}
    >
      {number}
    </button>
  )
}

export default ListPagination

개선점

  • 페이지 숫자 보이는 개수도 넘겨 받아서 사용할 수 있게 하면 더 유동적으로 쓰일 수 있을 것 같다.
  • 첫페이지와 마지막 페이지로 이동하는 버튼이 추가로 있어도 좋을 것 같고, 이 버튼이 나타나는지 여부도 프롭스로 넘겨받을 수 있겠다.

Categories:

Updated:

Leave a comment