Pagination은 웹 사이트에서 데이터나 콘테츠를 여러 페이지를 분리하는 방법이다.
위와 같이 데이터나 콘텐츠를 여러 페이지로 나누어 보여주는 것이다.
코드로 보면 다음과 같다.
const [startPage, setStartPage] = useState(1);
const { data, refetch } = useQuery(FETCH_BOARDS);
const onClickPage = (event: any) => {
refetch({ page: Number(event.target.id) });
};
return (
<div>
/////////////// 목록 데이터 /////////////////
{new Array(10).fill(1).map((_, index) => // 새로운 배열
index + startPage <= lastPage ? (
<MyPage
key={index + startPage}
onClick={onClickPage}
id={String(index + startPage)}
>
{index + startPage}
</MyPage>
) : (
<span></span>
)
)}
</div>
)
먼저 위에 보이는 목록 데이터를 불러왔다고 가정을 하고 보면, 일단 보여주고 싶은 페이지의 개수만큼 새로운 배열을 만들어 준 후에 배열 전체에 값을 주기 위해 map을 사용한다.
그리고 index + startPage를 보여준다. 이때 startPage 초깃값을 1로 줘야 한다.
그 이유는 index는 0부터 시작하기에 초깃값에 1을 줘서 0부터 데이터를 받아오지 않을 수 있다.
이제 클릭했을 때 이벤트를 주기 위해 onClick을 주고 refetch가 되는 함수를 실행해 준다.
refetch를 주지 않으면 page가 바뀌어도 렌더링 되지 않기 때문에 refetch를 해주어야 한다.
const [startPage, setStartPage] = useState(1);
const { data, refetch } = useQuery(FETCH_BOARDS);
const onClickPage = (event: any) => {
refetch({ page: Number(event.target.id) });
};
const onClickPrevPage = (event: any) => {
setStartPage((prev) => prev - 10);
refetch({ page: startPage - 10 });
};
const onClickNextPage = (event: any) => {
setStartPage((prev) => prev + 10);
refetch({ page: startPage + 10 });
};
return (
<div>
/////////////// 목록 데이터 /////////////////
<MyPage onClick={onClickPrevPage}>이전 페이지</MyPage>
{new Array(10).fill(1).map((_, index) => // 새로운 배열
index + startPage <= lastPage ? (
<MyPage
key={index + startPage}
onClick={onClickPage}
id={String(index + startPage)}
>
{index + startPage}
</MyPage>
) : (
<span></span>
)
)}
<MyPage onClick={onClickNextPage}>다음 페이지</MyPage>
</div>
)
이제 다음 페이지로 넘어가기 위한 버튼과 이전 페이지로 넘어가기 위한 버튼을 만들어준다.
클릭했을 때 함수가 실행되면서 setStartPage에 값을 -10 과 +10을 이용해 업데이트를 해준다.
이때도 refetch를 같이 해줘야 하기 때문에 refetch 함수도 같이 실행될 수 있게 해준다.
하지만 이렇게만 해주면 문제가 생길 수 있다.
1페이지나 데이터가 있는 마지막 페이지 그 이상 넘어가게 된다.
const [startPage, setStartPage] = useState(1);
const { data, refetch } = useQuery(FETCH_BOARDS);
const { data: dataBoardsCount } = useQuery(FETCH_BOARDS_COUNT);
const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);
const onClickPage = (event: any) => {
refetch({ page: Number(event.target.id) });
};
const onClickPrevPage = (event: any) => {
if (startPage === 1) return;
setStartPage((prev) => prev - 10);
refetch({ page: startPage - 10 });
};
const onClickNextPage = (event: any) => {
if (startPage + 10 >= lastPage) return;
setStartPage((prev) => prev + 10);
refetch({ page: startPage + 10 });
};
return (
/////////////// 목록 데이터 /////////////////
<div>
<MyPage onClick={onClickPrevPage}>이전 페이지</MyPage>
{new Array(10).fill(1).map((_, index) => // 새로운 배열
index + startPage <= lastPage ? (
<MyPage
key={index + startPage}
onClick={onClickPage}
id={String(index + startPage)}
>
{index + startPage}
</MyPage>
) : (
<span></span>
)
)}
<MyPage onClick={onClickNextPage}>다음 페이지</MyPage>
</div>
)
이를 해결하기 위해 다음과 같이 조건을 걸어줘서 조건에 걸리면 바로 return 하게 돼서 setState에 값이 업데이트되지 않는다.
마지막 페이지의 조건 같은 경우는 페이지 데이터의 카운트를 불러와 나누기 원하는 페이지만큼 해준 후 숫자를 올림 해주는 Math.ceil 메서드를 사용해서 올림 한 페이지를 변수에 할당해 준다.
그리고 그 변수보다 크거나 같다면 함수가 바로 종료되게 하면 된다.
이때 또 주의해야 할 점으로 올림 한 숫자기 때문에 데이터가 없는 페이지까지 보여줄 경우가 있기 때문에 웹사이트에 렌더 해주는 맵을 사용하는 부분에 한 번 더 조건을 걸어 페이지에 수만큼 돌고 조건에 걸린다면 빈 곳을 보여주게 하면 된다.
이렇게 되면 위에 gif에 보이는 화면과 같이 잘 동작하는 것을 볼 수 있다
Infinite Scroll은 사용자가 페이지 가장 아래에 도달할 때 데이터나 콘텐츠가 계속 로드가 되게 하는 방식이다.
이 방법은 페이스북, 인스타그램, 트위터 와 같은 소셜 미디어 웹사이트에서 이름을 알렸다.
위는 react-infinite-scroller(https://www.npmjs.com/package/react-infinite-scroller)라는 라이브러리를 사용하였다.
코드로 보면 다음과 같다.
import InfiniteScroll from "react-infinite-scroller";
const { data, fetchMore } = useQuery(FETCH_BOARDS);
console.log(data);
const onLoadMore = () => {
if (!data) return;
fetchMore({
variables: { page: Math.ceil(data.fetchBoards.length / 10) + 1 },
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult.fetchBoards)
return {
fetchBoards: [...prev.fetchBoards],
};
return {
fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
};
},
});
};
return (
<div style={{ height: "1500px", overflow: "auto" }}>
<InfiniteScroll
pageStart={0}
loadMore={onLoadMore}
hasMore={true}
useWindow={false}
>
{data?.fetchBoards.map((el) => (
<MyRow key={el._id}>
<MyColumn>{el._id}</MyColumn>
<MyColumn>{el.title}</MyColumn>
<MyColumn>{el.writer}</MyColumn>
</MyRow>
)) || <div></div>}
</InfiniteScroll>
</div>
);
};
먼저 라이브러리를 사용하기 위해서 npm에서 설치나 추가를 해줘야 한다.
그 후에 InfiniteScroll을 import 해준다.
그리고 그 사이에 보여줄 데이터 아이템을 넣어준다.
이제 동작하게 하기 위해 제공해 준 loadMore에 실행시킬 함수를 넣어준다.
함수 실행의 동작을 보면 먼저 fetchMore로 데이터를 불러온다.
variables에 들어오는 값으로는 데이터를 10으로 나눈 후에 올림 해준 숫자 더하기 1을 해준다.
이렇게 되면 데이터가 하나씩 추가될 수 있다.
updateQuery로 이전 데이터와 추가된 데이터를 불러온다.
이때 조건을 걸어 추가된 데이터가 없다면 이전 데이터를 바로 리턴해주는 조건을 걸어준다.
그 조건에 걸리지 않으면즉 추가된 데이터가 있다면 spread(전개 구문)을 이용해 이전 데이터와 추가 데이터를 추가해서 받아오게 된다.
이렇게 되면 위 gif 파일에 보이는 것과 같이 작동하게 된다.
전개 구문(spread)을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시킬 수 있습니다. *mdn