Pagination.js
// Pagination.js
import React from 'react';
// Pagination 컴포넌트는 여러 props를 받아 페이지네이션과 관련된 다양한 기능을 제공합니다.
function Pagination({
currentPage, // 현재 페이지 번호
totalPages, // 전체 페이지 수
itemsPerPage, // 한 페이지당 표시할 항목 수
totalItems, // 전체 항목 수
isLoading, // 데이터 로딩 상태
pageInputValue, // 직접 입력한 페이지 번호의 값
handlePage, // 페이지 이동 시 호출되는 핸들러 함수
handleItemsPerPageChange, // 페이지당 항목 수 변경 시 호출되는 핸들러
handlePageInputChange, // 페이지 번호 입력 시 호출되는 핸들러
handleDeleteSelected, // 선택된 항목 삭제를 위한 핸들러 (선택 삭제 기능)
selectedItems, // 현재 선택된 항목 배열
showFilters = true, // 필터 영역을 보여줄지 여부 결정하는 옵션 (기본값 true)
}) {
return (
// 전체 컨테이너: showFilters가 false인 경우, 페이지네이션 영역을 가운데 정렬함
<div
className="pagination-container"
style={{ justifyContent: !showFilters ? 'space-around' : 'space-between' }}
>
{/* 좌측 영역: 페이지당 항목 수 선택 및 선택 삭제 버튼 (showFilters가 true일 때만 표시) */}
{
showFilters && (
<div className="pagination-sub left">
{/* 선택된 항목이 있을 경우에만 선택 삭제 버튼을 표시 */}
{Array.isArray(selectedItems) && selectedItems.length > 0 && (
<button className="box mr10 color_border red" onClick={handleDeleteSelected}>
<i className="bi bi-trash3"></i>{selectedItems.length}건 삭제
</button>
)}
{/* 한 페이지당 표시할 항목 수를 입력받는 인풋 */}
<input
type="number"
id="itemsPerPage"
className="box"
value={itemsPerPage}
onChange={handleItemsPerPageChange}
min={1}
max={100}
step={1}
/>
{/* 인풋 옆에 현재 항목 수 및 전체 항목 수 표시 */}
<label htmlFor="itemsPerPage">
건씩 보기 / <b>{isLoading ? '-' : totalItems}</b>건
</label>
</div>
)
}
{/* 중앙 영역: 페이지네이션 버튼 */}
<div className="pagination">
{/* '처음' 버튼: 현재 페이지가 1보다 클 경우에만 표시 */}
{currentPage > 1 && (
<button className="box icon first" onClick={() => handlePage(1)}>
<i className="bi bi-chevron-double-left"></i>
</button>
)}
{/* '이전' 버튼: 현재 페이지가 1보다 클 경우에만 표시 */}
{currentPage > 1 && (
<button className="box icon left" onClick={() => handlePage(currentPage - 1)}>
<i className="bi bi-chevron-left"></i>
</button>
)}
{/* 페이지 번호 블록: 최대 5개의 페이지 번호를 동적으로 생성 */}
{Array.from({ length: Math.min(5, totalPages) }, (_, index) => {
// 현재 페이지 그룹의 시작 번호를 계산 (예: 1~5, 6~10 등)
const startPage = Math.max(Math.floor((currentPage - 1) / 5) * 5 + 1, 1);
const page = startPage + index;
return (
page <= totalPages && (
<button
key={page}
onClick={() => handlePage(page)}
className={currentPage === page ? 'box active' : 'box'}
>
{page}
</button>
)
);
})}
{/* '다음' 버튼: 현재 페이지가 전체 페이지 수보다 작을 경우에만 표시 */}
{currentPage < totalPages && (
<button className="box icon right" onClick={() => handlePage(currentPage + 1)}>
<i className="bi bi-chevron-right"></i>
</button>
)}
{/* '끝' 버튼: 현재 페이지가 전체 페이지 수보다 작을 경우에만 표시 */}
{currentPage < totalPages && (
<button className="box icon last" onClick={() => handlePage(totalPages)}>
<i className="bi bi-chevron-double-right"></i>
</button>
)}
</div>
{/* 우측 영역: 페이지 번호 직접 입력 (showFilters가 true일 때만 표시) */}
{
showFilters && (
<div className="pagination-sub right">
{/* 직접 페이지 번호를 입력할 수 있는 인풋 */}
<input
type="number"
id="pageInput"
className="box"
value={pageInputValue}
onChange={handlePageInputChange}
min={1}
max={totalPages}
step={1}
/>
<label htmlFor="pageInput">/ <b>{totalPages}</b>페이지</label>
</div>
)
}
</div >
);
}
export default Pagination;
동작 원리
- 컨테이너 스타일: showFilters 값에 따라 좌측(필터/항목 수, 선택 삭제)와 우측(페이지 번호 입력) 영역의 정렬 방식 조정
- 선택 삭제 기능: selectedItems 배열의 길이에 따라 선택 삭제 버튼이 표시되며 클릭 시 handleDeleteSelected 함수 호출
- 항목 수 선택: 사용자가 페이지당 몇 개의 항목을 볼지를 선택할 수 있도록 숫자 인풋을 제공하며, 변경 시 handleItemsPerPageChange 함수 호출
- 페이지네이션 버튼: 현재 페이지와 전체 페이지 수를 기반으로 '처음', '이전', 개별 페이지 번호, '다음', '끝' 버튼을 조건부로 렌더링함. 사용자가 버튼을 클릭하면 handlePage 함수가 호출되어 해당 페이지로 이동
- 페이지 입력: 직접 페이지 번호를 입력할 수 있는 인풋을 제공해 변경 시 handlePageInputChange 함수 호출
이거 임포트해서 쓰면 좋았을텐데, 프로젝트 당시 시간도 없고 급한 마음에 아래 처럼 단순화 해서 처리 시켰다 🥲
페이지네이션 부분 단순화
// 단순화된 페이지네이션 영역
<div className="pagination">
{/* 페이지 번호가 1보다 큰 경우 '처음'과 '이전' 버튼 표시 */}
{page > 1 && (
<>
<button className="box icon first" onClick={() => PageChange(1)}>
<i className="bi bi-chevron-double-left"></i>
</button>
<button className="box icon left" onClick={() => PageChange(page - 1)}>
<i className="bi bi-chevron-left"></i>
</button>
</>
)}
{/* 최대 5개의 페이지 번호 버튼을 생성 */}
{Array.from({ length: Math.min(5, totalPages) }, (_, index) => {
// 현재 페이지 그룹의 시작 번호 계산 (예: 1~5, 6~10 등)
const startPage = Math.floor((page - 1) / 5) * 5 + 1;
const currentPage = startPage + index;
return (
currentPage <= totalPages && (
<button
key={currentPage}
onClick={() => PageChange(currentPage)}
className={currentPage === page ? 'box active' : 'box'}
>
{currentPage}
</button>
)
);
})}
{/* 페이지 번호가 전체 페이지 수보다 작은 경우 '다음'과 '끝' 버튼 표시 */}
{page < totalPages && (
<>
<button className="box icon right" onClick={() => PageChange(page + 1)}>
<i className="bi bi-chevron-right"></i>
</button>
<button className="box icon last" onClick={() => PageChange(totalPages)}>
<i className="bi bi-chevron-double-right"></i>
</button>
</>
)}
</div>
<div className="pagination-sub right"></div>
동작 원리
- 조건부 렌더링:
- page가 1보다 클 때 '처음'과 '이전' 버튼을 묶어서 표시
- page가 전체 페이지 수보다 작을 때, '다음'과 '끝' 버튼을 묶어서 표시
- 페이지 번호 버튼 생성:
- Array.from을 사용하여 최대 5개의 페이지 번호를 생성하며
- 현재 페이지 그룹(예를 들어, 1
5, 610 등)의 시작 번호를 계산하여 버튼을 렌더링 - 각 버튼 클릭 시 PageChange 함수가 호출되어 해당 페이지로 이동
- 추가 영역: 우측의 pagination-sub right 영역은 이 코드에서는 빈 영역으로 남겨 추가적인 기능(예: 페이지 직접 입력) 등을 넣을 여지를 제공
App.js에서 Pagination 사용하려면?
👉 import 후에 JSX 안에서 필요한 props를 전달하며 컴포넌트를 사용
import Pagination from './main/react/components/common/Pagination';
function App() {
const currentPage = 1;
const totalPages = 10;
// 기타 필요한 상태와 핸들러 선언
return (
<div>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
itemsPerPage={10}
totalItems={100}
isLoading={false}
pageInputValue={currentPage}
handlePage={(page) => console.log('페이지 변경:', page)}
handleItemsPerPageChange={(e) => console.log('페이지당 항목 수 변경:', e.target.value)}
handlePageInputChange={(e) => console.log('페이지 입력 변경:', e.target.value)}
handleDeleteSelected={() => console.log('선택된 항목 삭제')}
selectedItems={[]}
showFilters={true}
/>
</div>
);
}
export default App;
지금보면 임포트해서 쓰는 간단한건데 프로젝트 할때는 막무가내로 단순화 시켜서 했었다. 면접 준비 하다가 아쉬운 마음이 들어 포스팅해본다.
'개발인생 > Project' 카테고리의 다른 글
페이징처리 프로젝트 구현(Oracle) (0) | 2025.03.17 |
---|---|
페이징처리 - Pagination.js + Pageable (0) | 2025.03.17 |
Spring Security 설정 완벽 분석 - 보안 설정 가이드 (2) | 2025.03.05 |
프로젝트 코드 리뷰: JPQL vs QueryDSL 최적화 및 리팩토링 (0) | 2025.02.21 |
JSP/Servlet 프로젝트와 React+Spring Boot 프로젝트 비교 (1) | 2025.02.19 |