
items : 받아오는 데이터◾ 페이지 네이션 프롭
itemCount : 한번에 보여지는 리스트 개수totalCount : api로 받아온 전체 아이템 카운터 갯수◾ 페이지네이션 컴포넌트에서 쓰이는 변수들
totalPages : 전체 페이지 수 const totalPages = Math.ceil(totalCount / itemCount);
isFirstPage : 제일 첫페이지 isLastPage : 마지막 페이지 const isFirstPage = selectedPage === 1;
const isLastPage = selectedPage === totalPages;
handlePageClick : 페이지 클릭시 데이터 처리되는 함수◾ 전체페이지수를 5페이지씩 보여주는 hook
usePaginationtotalPages, page const [pageNumbers, setPageNumbers] = useState<number[]>([]);
페이지 네이션컴포넌트를 쓰는 페이지에서 서버사이드 렌더링을 위한 데이터를 가져오는 과정을 거친후 (fetchNoticeData, fetchShopData, fetchTableData 함수들)
페이지가 로드 될때마다 getServerSideProps 함수에서 위에 정의한 데이터가 비동기처리되어 가져오고 context 를 통해 쿼리에서 page를 가져온다.
2-2. fetchTableData에서는 페이지네이션을 위해 offset과 - limit을 설정하여 데이터를 가져옵니다.
각각의 데이터를 가져온 후에는 결과를 처리(이과정은 프로젝트 서버사이드 구현 참고)
sliceNumber에서 정의된 숫자만큼 배열을 자른다.
// usePagenation.tsx
const handleSlicePage = useCallback(() => {
const array = Array.from({ length: totalPages }, (_, i) => i + 1);
// 자른 페이지 넘버들을 넣는 변수
const slice: number[][] = [];
for (let i = 0; i < array.length; i += sliceNumber) {
// 자르는 로직
const sliceItem = array.slice(i, i + sliceNumber);
// 자른 배열을 넣기
slice.push(sliceItem);
}
// eslint-disable-next-line no-restricted-syntax
for (const row of slice) {
if (row.includes(page)) {
setPageNumbers([...row]);
break;
}
}
}, [totalPages, page]);
useEffect(() => {
handleSlicePage();
}, [page, handleSlicePage]);
return { pageNumbers };
}
shallow 옵션을 false로 설정해서 다시한번 서버 사이드 함수가 실행되게 한다.예> 2 클릭시 ?page=2 로 이동
handlePageClick(
setSelectedPage(pageNumber);
//쿼리를 클릭한 페이지로 변경
await router.replace(
{
pathname,
query: { ...query, page: pageNumber.toString() },
},
undefined,
{
shallow: false, // getServerSideProps는 같은 주소(pathname)일 경우 한번만 호출되서 이 옵션을 false로 바꿔서 호출되게 변경
scroll: false,
},
);
)
<div className={styles.pageNumberBox}>
{Array.from({ length: totalPages }, (_, i) => i + 1).map(num => (
<div
className={`${styles.pageNumber} ${
num === selectedPage ? styles.selected : ''
}`}
key={num}
onClick={() => handlePageClick(num)}
>
{num}
</div>
))}
</div>
✅ 페이지 수에 따른 페이지 넘버를 생성하고 매핑하는 부분입니다.
{Array.from({ length: totalPages }, (_, i) => i + 1).map(num => ( ... ))}
Array.from() 메서드를 사용하여 페이지 수 만큼 배열을 생성하고, map() 함수를 사용하여 각 페이지 넘버를 생성합니다.
_는 현재 요소를 나타내며, i는 현재 요소의 인덱스를 나타냅니다. 우리는 인덱스를 1부터 시작하는 페이지 넘버로 변환하기 위해 i + 1을 반환합니다.
⭐ Array.from() 의 첫 번째 인자는 배열로 만들 이터러블한 객체가 되며, 두 번째 인자는 생성한 배열의 모든 원소에 대해 수행할 맵핑 함수입니다. (Array.map() 이라고 생각하시면 됩니다.)
*언더스코어(_) 는 특별한 인자가 아니라, 불필요한 인자의 공간을 채우기 위한 용도입니다.
=> 페이지수가 많아지면 전체페이지가 한줄로 길게 나와서 5개씩 보이도록 코드 수정
<div className={styles.pageNumberBox}>
{pageNumbers.map(num => (
<div
tabIndex={0}
className={`${styles.pageNumber} ${
num === selectedPage ? styles.selected : ''
}`}
key={num}
role="presentation"
onClick={() => handlePageClick(num)}
onKeyDown={event => {
if (event.key === 'Enter') {
handlePageClick(num);
}
}}
>
{num}
</div>
))}
</div>
맨첫페이지와 맨끝페이지는 버튼이 회색이 되게 만든다.
const isFirstPage = selectedPage === 1;
const isLastPage = selectedPage === totalPages;
...
{isFirstPage ? (
<LeftButton className={styles.icon} tabIndex={0} />
) : (
<LeftButtonOn
className={styles.icon}
onClick={() => !isFirstPage && handlePageClick(selectedPage - 1)}
/>
)}
{isLastPage ? (
<RightButton className={styles.icon} tabIndex={0} />
) : (
<RightButtonOn
tabIndex={0}
className={styles.icon}
onClick={() => !isLastPage && handlePageClick(selectedPage + 1)}
/>
)}