페이지네이션이란 여러개의 컨텐츠를 여러 페이지로 나누고 페이지 번호 버튼, 이전 버튼, 다음 버튼을 눌러서 페이지를 이동하는 기능이다.
페이지 네이션에는 총 페이지 개수, 화면에 보여질 페이지 그룹, 화면에 보여질 페이지의 첫번쨰 페이지 번호, 화면에 보여질 페이지의 마지막 페이지 번호의 값이 필요하다.
😢처음에 하는 방법을 몰라 전체 데이터를 가지고 pagination을 만들어 버렸다. 이렇게 전체 데이터를 가지고 pagination을 만들다보면 서버가 터져버리는 이슈가 발생할 수 있다. 그걸 모르고 만들다가 아주 고생을 해버렸다. 두번 일하게 되어버린..
아래 코드는 size와 page를 axios요청을 보내서 서버에서 데이터를 받는 로직이다.
//받은 데이터들
const [memberList, setMemberList] = useState([]);
// 작성날짜 순으로 데이터들 나열해서 받는 sort
const [sort] = useState(`sort=createDate,desc`);
// 페이지 숫자
const [page, setPage] = useState(0);
// 전체 데이터 리스트 개수
const [totalPages, setTotalPages] = useState(0);
//한 페이지당 받는 데이터 수
const [size] = useState(7);
//검색리스트 길이
const [listLength, setListLength] = useState(0);
useEffect(() => {
const getData = async () => {
await axios({
method: 'get',
url: `${process.env.REACT_APP_API_URL}/itemQna/list?
size=${size}&page=${page}&${sort}`,
}).then((res) => {
setMemberList(res.data.content);
setTotalPages(res.data.totalElements);
setListLength(res.data.numberOfElements);
});
};
getData();
}, [sort, size, page]);
이렇게 받은 데이터를 브라우저에 띄어야한다.
// 여기서 data는 memberList이다.
data.map((el: any, index: any) => {
return (
<S.Container key={index} onClick={() => moveQnApw(el.id)}>
<div>{el.id}</div>
<div>
<ul>{el.title}</ul>
<TfiLock color='#D9D9D9' />
</div>
{isDesktopOrMobile !== true ? (
<div>
<p>{el.memberId}</p>
<p>{registDate2(el.createDate)}</p>
</div>
) : (
<div>
<ul>{el.memberId}</ul>
<ul>{registDate2(el.createDate)}</ul>
</div>
)}
</S.Container>
);
})
이런 코드를 작성하면 화면에 서버에서 받아온 데이터들이 브라우저 상에 나온다. 그렇다면 이제 pagination을 만들어야한다.
나는 pagination을 컴포넌트화해서 따로 빼주었다.
//현재페이지
const [currentPage, setCurrentPage] = useState(props.page + 1);
//페이지당 보여지는 리스트
const [itemsPerPage] = useState(props.size);
//브라우저상 보여지는 한계 숫자
const [pageNumberLimit] = useState(5);
const [maxPageNumberLimit, setMaxPageNumberLimit] = useState(5);
const [minPageNumberLimit, setMinPageNumberLimit] = useState(0);
<Pagination
// 여기서 data는 memberList이다.
data={props.data}
totalPages={props.totalPages}
page={props.page}
setPage={props.setPage}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
itemsPerPage={itemsPerPage}
pageNumberLimit={pageNumberLimit}
maxPageNumberLimit={maxPageNumberLimit}
setMaxPageNumberLimit={setMaxPageNumberLimit}
minPageNumberLimit={minPageNumberLimit}
setMinPageNumberLimit={setMinPageNumberLimit}
/>
for문을 돌려 1부터 전체 페이지까지 pages 배열에 담아주었다.
const pages = [];
for (let i = 1; i <= Math.ceil(props.totalPages / props.itemsPerPage); i++) {
pages.push(i);
}
pages 배열을 map으로 순회해 해당 number 값이 브라우저 상에 보여지는 최대 숫자(5) 이하이거나 최소숫자(0) 초과일때 브라우저 상에 해당 number값을 띄우도록 로직을 구현하였다. 해당 number을 눌렀을 시 그 값이 useState로 값을 설정한 현재 페이지(currentPage)에 넣어주도록 설정하였다.
const handleClick = (event: any) => {
props.setCurrentPage(Number(event.target.id));
props.setPage(event.target.id - 1);
};
const renderPageNumbers = pages.map((number: any) => {
if (number < props.maxPageNumberLimit + 1 && number > props.minPageNumberLimit) {
return (
<S.Paging2
key={number}
id={number}
onClick={handleClick}
style={number === props.currentPage ? totalColor : {}}
>
{number}
</S.Paging2>
);
} else {
return null;
}
});
다음버튼을 눌렀을 시 현재 페이지에서 1을 추가해준다.
만약 현재페이지에서 1 더한 값이 브라우저상에서 보여지는 최대 숫자값(maxPageNumberLimit)보다 클 경우 브라우저 상에 보여지는 최대 숫자값과 최소 숫자값에 각각 5를 더해 늘려준다.
const handleNextbtn = () => {
props.setCurrentPage(props.currentPage + 1);
props.setPage(props.page + 1);
if (props.currentPage + 1 > props.maxPageNumberLimit) {
props.setMaxPageNumberLimit(props.maxPageNumberLimit + props.pageNumberLimit); // 5+5 =10
props.setMinPageNumberLimit(props.minPageNumberLimit + props.pageNumberLimit); //0+5 = 5
}
};
다음버튼을 눌렀을 시 현재 페이지가 pages배열의 0번재 인덱스랑 같다면 disabled 처리해준다. 즉, 처음 페이지라면 클릭 못하도록 설정
disabled={props.currentPage === pages[0] ? true : false}
이전버튼을 눌렀을 시 현재 페이지에서 1을 빼준다.
만약 현재페이지에서 1을 빼준 값을 5로 나눴을 때 나머지가 0이라면 브라우저 상에 보여지는 최대 숫자값과 최소 숫자값에 각각 5를 빼준다.
const handlePrevbtn = () => {
props.setCurrentPage(props.currentPage - 1);
props.setPage(props.page - 1);
if ((props.currentPage - 1) % props.pageNumberLimit === 0) {
props.setMaxPageNumberLimit(props.maxPageNumberLimit - props.pageNumberLimit);//10 -5 =5
props.setMinPageNumberLimit(props.minPageNumberLimit - props.pageNumberLimit); // 5-5 =0
}
};
이전 버튼을 눌렀을 시 현재 페이지가 pages 배열의 마지막 인덱스와 같다면 disabled 처리 해준다. 즉, 마지막 페이지라면 클릭 안되도록 설정
disabled={props.currentPage === pages[pages.length - 1] ? true : false}
import { AiOutlineLeft, AiOutlineRight } from 'react-icons/ai';
import * as S from './style';
import React from 'react';
export const Pagination = (props: any) => {
const numberColor = '#289951';
const numbertxtColor = 'white';
const totalColor = {
backgroundColor: numberColor,
color: numbertxtColor,
};
const pages = [];
for (let i = 1; i <= Math.ceil(props.totalPages / props.itemsPerPage); i++) {
pages.push(i);
}
const handleClick = (event: any) => {
props.setCurrentPage(Number(event.target.id));
props.setPage(event.target.id - 1);
};
const renderPageNumbers = pages.map((number: any) => {
if (number < props.maxPageNumberLimit + 1 && number > props.minPageNumberLimit) {
return (
<S.Paging2
key={number}
id={number}
onClick={handleClick}
style={number === props.currentPage ? totalColor : {}}
>
{number}
</S.Paging2>
);
} else {
return null;
}
});
const handleNextbtn = () => {
props.setCurrentPage(props.currentPage + 1);
props.setPage(props.page + 1);
if (props.currentPage + 1 > props.maxPageNumberLimit) {
props.setMaxPageNumberLimit(props.maxPageNumberLimit + props.pageNumberLimit);
props.setMinPageNumberLimit(props.minPageNumberLimit + props.pageNumberLimit);
}
};
const handlePrevbtn = () => {
props.setCurrentPage(props.currentPage - 1);
props.setPage(props.page - 1);
if ((props.currentPage - 1) % props.pageNumberLimit === 0) {
props.setMaxPageNumberLimit(props.maxPageNumberLimit - props.pageNumberLimit);
props.setMinPageNumberLimit(props.minPageNumberLimit - props.pageNumberLimit);
}
};
return (
<S.PageNumbers>
<S.LiPageNumbers>
<S.ButtonPageNumbers
onClick={handlePrevbtn}
disabled={props.currentPage === pages[0] ? true : false}
>
<AiOutlineLeft size='30' />
</S.ButtonPageNumbers>
</S.LiPageNumbers>
{renderPageNumbers}
<S.LiPageNumbers>
<S.ButtonPageNumbers
onClick={handleNextbtn}
disabled={props.currentPage === pages[pages.length - 1] ? true : false}
>
<AiOutlineRight size='30' />
</S.ButtonPageNumbers>
</S.LiPageNumbers>
</S.PageNumbers>
);
};
import styled from 'styled-components';
export const PageNumbers = styled.ul`
list-style: none;
display: flex;
align-items: center;
margin-bottom: 1.2rem;
justify-content: center;
@media screen and (max-width: 720px) {
margin-bottom: 0;
}
`;
export const LiPageNumbers = styled.li`
padding: 0.1rem;
cursor: pointer;
&:active {
color: black;
}
`;
export const ButtonPageNumbers = styled.button`
background-color: transparent;
border: none;
font-size: 0.17rem;
margin-bottom: 0.6rem;
cursor: pointer;
&:hover {
color: black;
}
&:focus {
outline: none;
}
@media screen and (max-width: 720px) {
margin-bottom: 0;
font-size: 0.5rem;
}
`;
export const Paging2 = styled.li`
padding: 0.1rem;
cursor: pointer;
font-size: 0.17rem;
color: ${({ theme }) => theme.palette.txtgray};
&:active {
background-color: ${({ theme }) => theme.palette.green};
cursor: pointer;
color: white;
}
@media screen and (max-width: 720px) {
font-size: 0.5rem;
padding: 5% 10%;
}
`;