GraphQL
[GraphQL] Pagination, Infinite Scroll
GraphQL에서 페이지네이션과 무한 스크롤을 구현하는 방법을 정리합니다.

Pagination
**페이지네이션**은 보여 줄 데이터가 많을 때 한 번에 불러오지 않고, 페이지 번호를 눌러 이동하는 방식입니다.
GraphQL에서는 refetch 함수로 구현할 수 있습니다.
export default function PaginationPage() {
const [startPage, setStartPage] = useState(1); // 초기값 설정 (index번호는 0부터 시작이기때문에)
const { data, refetch } = useQuery<
Pick<IQuery, "fetchBoards">,
IQueryFetchBoardsArgs
>(FETCH_BOARDS);
const onClickPage = (event: MouseEvent<HTMLSpanElement>) => {
// refetch(page : 보여줄 페이지번호)
void refetch({ page: Number(event.currentTarget.id) });
};
return (
<div>
{data?.fetchBoards?.map((el) => (
// data?. => 데이터가 있으면 / fetchBoards?. => fetchBoards가 있으면 map메소드 실행
<div key={el._id}>
<span >{el.title}</span>
<span >{el.contents}</span>
</div>
))}
<PageNationWrap>
{new Array(10).fill(0).map(
// 초기에 반복할 갯수가 없기에 new Array로 반복할 횟수를 설정
// new Array(10)만하면 빈값이기에 fill(0)으로 모든 배열에 값 넣어주기(빈값이면 반복문이 돌지 않아요)
(_, index) => (
// _는 사용하지 않는 매개변수일경우 작성
<PageButton
key={index + startPage}
id={String(index + startPage)}
onClick={onClickPage}
>
{index + startPage}
</PageButton>
)
)}
</PageNationWrap>
</div>
);
}
여기까지가 페이지네이션의 기본입니다. 여기에 이전·다음 버튼을 만들고, 데이터가 없는 페이지 번호는 보이지 않도록 설정을 추가했습니다.
const [isPage, setIsPage] = useState(1); // 현재 보고 있는 페이지
const { data: dataBoardsCount } = useQuery<
Pick<IQuery, "fetchBoardsCount">,
IQueryFetchBoardsCountArgs
>(FETCH_BOARDS_COUNT);
// boards의 총 갯수
const lastPage =
dataBoardsCount != null
? Math.ceil(dataBoardsCount?.fetchBoardsCount / 10)
: 1;
// 마지막 페이지 구하기
// 만약에 796개의 리스트가 있다면 반올림하여 80개의 페이지가 마지막 페이지
const onClickPrevPage = () => { // 이전페이지
if (startPage === 1) return; // 첫번째 페이지면 작동금지 (early return 패턴)
setStartPage(startPage - 10); // 10씩 줄이기 (페이지네이션이 10개를 노출 하기에)
void refetch({ page: startPage - 10 });
setIsPage(startPage - 10);
};
const onClickNextPage = () => { // 이전페이지
if (startPage + 10 <= lastPage) { // 라스트 페이지를 넘기기전까지만 작동 => (71 + 10 <= 80) false
setStartPage(startPage + 10);
void refetch({ page: startPage + 10 });
setIsPage(startPage + 10);
}
};
위 코드를 추가해 이전·다음 버튼과 현재 페이지 표시(active)까지 구현했습니다.
최종 코드
export default function StaticRoutedPage() {
const [startPage, setStartPage] = useState(1);
const [isPage, setIsPage] = useState(1);
const { data, refetch } = useQuery<
Pick<IQuery, "fetchBoards">,
IQueryFetchBoardsArgs
>(FETCH_BOARDS);
const { data: dataBoardsCount } = useQuery<
Pick<IQuery, "fetchBoardsCount">,
IQueryFetchBoardsCountArgs
>(FETCH_BOARDS_COUNT);
const lastPage =
dataBoardsCount != null
? Math.ceil(dataBoardsCount?.fetchBoardsCount / 10)
: 1;
const onClickPage = (event: MouseEvent<HTMLSpanElement>) => {
void refetch({ page: Number(event.currentTarget.id) });
setIsPage(Number(event.currentTarget.id));
};
const onClickPrevPage = () => {
if (startPage === 1) return;
setStartPage(startPage - 10);
void refetch({ page: startPage - 10 });
setIsPage(startPage - 10);
};
const onClickNextPage = () => {
if (startPage + 10 <= lastPage) {
setStartPage(startPage + 10);
void refetch({ page: startPage + 10 });
setIsPage(startPage + 10);
}
};
return (
<div>
{data?.fetchBoards?.map((el) => (
<div key={el._id}>
<span style={{ margin: "10px" }}>{el.title}</span>
<span style={{ margin: "10px" }}>{el.contents}</span>
</div>
))}
<PageNationWrap>
<PageButton disabled={startPage === 1} onClick={onClickPrevPage}>
<LeftOutlined />
</PageButton>
{new Array(10)
.fill(0)
.filter((_, filterIndex) => filterIndex + startPage <= lastPage)
.map((_, mapIndex) => (
<PageButton
key={mapIndex + startPage}
id={String(mapIndex + startPage)}
onClick={onClickPage}
isPageActive={isPage === mapIndex + startPage ? true : false}
>
{mapIndex + startPage}
</PageButton>
))}
<PageButton disabled={startPage + 10 > lastPage} onClick={onClickNextPage}>
<RightOutlined />
</PageButton>
</PageNationWrap>
</div>
);
}
Infinite Scroll
유튜브나 페이스북처럼 페이지를 아래로 스크롤하다 끝에 도달하면
새 데이터가 계속 추가되는 방식을 **무한 스크롤**이라고 합니다.
React에서 많이 쓰는 무한 스크롤 라이브러리 중 react-infinite-scroller를 사용했습니다.
react infinite scroller
npm 사용자
npm install react-infinite-scroller --save
yarn 사용자
yarn add react-infinite-scroller
TypeScript를 사용한다면 타입도 함께 설치합니다.
npm 사용자
npm install --save-dev @types/react-infinite-scroller
yarn 사용자
yarn add --dev @types/react-infinite-scroller
무한 스크롤은 페이지네이션보다 구현이 간단하고 사용하기도 편하다고 느꼈습니다. 다만 원하는 페이지로 바로 이동할 수 없다는 단점이 있습니다.
무한 스크롤을 적용할 영역을 InfiniteScroll 컴포넌트로 감싸서 사용합니다.
코드로 이어서 설명하면 다음과 같습니다.
import InfiniteScroll from "react-infinite-scroller"; // 추가 해주세요
export default function InfiniteScrollPage() {
const { data, fetchMore } = useQuery<
Pick<IQuery, "fetchBoards">,
IQueryFetchBoardsArgs
>(FETCH_BOARDS);
// fetchMore를 추가 해주세요
const onFectchMore = () => {
if (data === undefined) return; // data가 없을경우 종료 (early return 패턴)
void fetchMore({
variables: {
page: Math.ceil(data.fetchBoards.length / 10) + 1,
// fetchBoards의 데이터는 계속 쌓임 그러면 10개씩 계속 증가
},
updateQuery(prev, { fetchMoreResult }) { // prev : 이전데이터 , fetchMoreResult : 쿼리 호출 후 결과
if (fetchMoreResult.fetchBoards === undefined) {
return {
fetchBoards: [...prev.fetchBoards],
};
} // 쿼리 호출후 결과가 undefined일 경우 이전값까지만 노출 (early return 패턴)
return {
fetchBoards: [...prev.fetchBoards, ...fetchMoreResult.fetchBoards],
}; // 이전 데이터와 호출후 데이터 합치기
},
});
};
return (
<div>
<InfiniteScroll
pageStart={0}
loadMore={onFectchMore} // 화면이 최하단으로 갔을경우 실행할 함수
hasMore={true}
>
{data?.fetchBoards?.map((el) => (
<div key={el._id}>
<span style={{ margin: "10px" }}>{el.title}</span>
<span style={{ margin: "10px" }}>{el.contents}</span>
</div>
))}
</InfiniteScroll>
</div>
);
}
댓글 0