
서버와 클라이언트 간의 페이지네이션을 구현하게 되면 기본적으로 서버에서 아래와 같이 데이터를 받기 때문에 보다 쉽게 페이지네이션을 구현할 수 있다.
{
"data": [
{ "id": 1, "name": "Item 1" },
{ "id": 2, "name": "Item 2" },
{ "id": 3, "name": "Item 3" }
// ... more items
],
"metadata": {
"totalItems": 100,
"currentPage": 1,
"totalPages": 10,
"pageSize": 10,
"hasNextPage": true,
"hasPreviousPage": false
}
}totalItems: 전체 데이터 항목의 수currentPage: 현재 페이지 번호totalPages: 전체 페이지 수pageSize: 한 페이지에 포함된 항목의 수hasNextPage: 다음 페이지 존재 여부hasPreviousPage: 이전 페이지 존재 여부
구현
목업 데이터
interface DataItem j{
id: number
name: string
email: string
}j
const datas: DataItem[] = Array.from({ length: 110 }, (_, i) => ({
id: i,
name: customFaker.person.fullName(),
email: customFaker.internet.email(),
}))fakerjs를 사용하여 목업 데이터를 생성.
페이지네이션 설정
const PAGINATION_CONFIG = {
ITEMS_PER_PAGE: 5,
VISIBLE_PAGES: 5,
} as constITEMS_PER_PAGE: 한 페이지에 보여질 데이터 수VISIBLE_PAGES: 보여질 페이지 범위
전체 페이지 수
const totalPages = Math.ceil(datas.length / PAGINATION_CONFIG.ITEMS_PER_PAGE)- 전체 데이터의 크기와 한 페이지에 보여질 데이터 수를 나누어 전체 페이지 수를 구한다
Example
- 총 데이터 수: 103개
- 페이지당 항목: 10개
- 계산: 103 ÷ 10 = 10.3 → 11 페이지
데이터
const [currentPage, setCurrentPage] = useState(1)
const startIndex = (currentPage - 1) * PAGINATION_CONFIG.ITEMS_PER_PAGE
const endIndex = startIndex + PAGINATION_CONFIG.ITEMS_PER_PAGE
const currentItems = datas.slice(startIndex, endIndex)현재 페이지에 해당하는 데이터를 계산하는 로직이다. 서버 통신을 할 때는 서버에서 해당 페이지의 데이터를 직접 제공하므로 이 계산이 필요 없을 수 있다.
페이지 범위

const startPage =
(Math.ceil(currentPage / PAGINATION_CONFIG.VISIBLE_PAGES) - 1) * PAGINATION_CONFIG.VISIBLE_PAGES +
1
const endPage = Math.min(startPage + PAGINATION_CONFIG.VISIBLE_PAGES - 1, totalPages)
const pageNumbers = Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i)startPage, endPage를 이용하여 페이지 범위에 대한 번호를 구할 수 있다.
Example
- VISIBLE_PAGES가 5일 때
- 현재 페이지가 7이면: 6, 7, 8, 9, 10이 표시
- 현재 페이지가 13이면: 11, 12, 13, 14, 15가 표시
endPage 계산 시 Math.min을 사용하는 이유는 마지막 페이지 그룹이 VISIBLE_PAGES보다 적은 페이지를 가질 수 있기 때문이다.
페이지 네비게이션 핸들러
const handlePageChange = (pageNumber: number) => {
if (currentPage === pageNumber) return
setCurrentPage(pageNumber)
}
const handlePrev = () => {
setCurrentPage((prev) => Math.max(1, prev - 1))
}
const handleNext = () => {
setCurrentPage((prev) => Math.min(totalPages, prev + 1))
}컴포넌트
<div className={styles.container}>
{/* 테이블 컴포넌트*/}
<div className={styles.tableContainer}>
<table className={styles.table}>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{currentItems.map((data) => (
<tr key={data.id}>
<td>{data.id}</td>
<td>{data.name}</td>
<td>{data.email}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* 페이지 핸들러 컴포넌트 */}
<div className={styles.pagination}>
<button
type="button"
onClick={handlePrev}
disabled={currentPage === 1}
className={styles.navigationButton}
>
Prev
</button>
{pageNumbers.map((number) => (
<button
key={number}
type="button"
onClick={() => handlePageChange(number)}
className={`${styles.pageButton} ${currentPage === number ? styles.active : ""}`}
>
{number}
</button>
))}
<button
type="button"
onClick={handleNext}
disabled={currentPage === totalPages}
className={styles.navigationButton}
>
Next
</button>
</div>
</div>