프로젝트에서 처음 주어진 과제는 랭킹 페이지 구현이었다.
랭크 리스트는 무한 스크롤이나 페이지네이션 등을 통해 나타낼 수 있다.
이번 포스트에서는 무한 스크롤 구현에 대해 설명한다.
무한 스크롤을 구현하기 위해 React Query을 사용한다.
리액트 쿼리는 서버의 값을 클라이언트에 가져오거나 캐싱, 값 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 하는데 사용한다.
리액트 쿼리를 사용하려면 컴포넌트를 QueryClientProvider로 감싸주어야 한다.
import { QueryClient, QueryClientProvider } from "react-query";
import RankList from '../components/rank/RankList';
export default function Rank() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<RankList />
</QueryClientProvider>
);
}
RankList컴포넌트 내부에서 데이터를 불러오고, 다음 페이지의 데이터를 불러오는 과정을 구현한다.
import axios from 'axios';
import React, { useEffect } from 'react';
import { Rank, RankData } from '../../types/rankTypes';
import infScroll from '../../utils/infinityScroll';
import { useInfiniteQuery } from 'react-query';
import InfiniteScroll from 'react-infinite-scroll-component';
const END_POINT = 'http://localhost:3000/api';
export default function infScroll(path: string) {
// pageParam은 useInfiniteQuery의 getNextPageParam에서 자동으로 넘어온다.
// 1페이지는 undefined로 아무것도 넘어오지 않는다. 초기값을 반드시 설정하자.
const getRankList = ({ pageParam = 1 }) =>
axios
.get(`${END_POINT}${path}${pageParam}`, {})
.then((res) => res?.data);
const result = useInfiniteQuery('rankList', getRankList, {
// 💡 중요! getNextPageParams가 무한 스크롤의 핵심,
// getNextPageParam 메서드가 falsy한 값을 반환하면 추가 fetch를 실행하지 않는다
// falsy하지 않은 값을 return 할 경우 Number를 리턴해야 하며
// 위의 fetch callback의 인자로 자동으로 pageParam을 전달.
getNextPageParam: (lastPage, pages) => {
if (lastPage.info.next === 'null') return false;
return pages.length + 1;
},
});
return result;
}
export default function RankList() {
const result = infScroll('/pingpong/ranks/');
const { data, // 💡 data.pages를 갖고 있는 배열
fetchNextPage, // 💡 다음 페이지를 불러오는 함수
hasNextPage, // 다음 페이지가 있는지 여부, Boolean
status // 💡 loading, error, success 중 하나의 상태, string
} = result;
return (
<>
<InfiniteScroll
dataLength={data?.pages.length! * 8}
next={fetchNextPage}
hasMore={hasNextPage!}
loader={<h4>Loading...</h4>}
>
<div className={styles.title}>Ranking</div>
<div className={styles.container}>
... 중략 ...
</div>
{status === 'success' && (
<>
{data?.pages.map((group, index) => (
... 중략 ...
))}
</>
)}
</div>
</InfiniteScroll>
</>
);
}
현재 구현된 무한 스크롤에는 데이터가 변경될 때 중복 또는 누락의 문제가 발생한다. (e.g 10개씩의 데이터를 불러올 경우 10등이 11등이 되면 기존 10위와 새로 불러온 데이터의 11위가 중복)
-> 소켓 통신 등의 실시간 통신을 하지 않고 정적인 데이터를 주고 받기 때문에 발생하는 문제다. 하여 무한스크롤보다는 페이지네이션으로 문제 발생을 최소화하기로 했다.
데이터가 많아져서 해당 순위까지 이동하는데 소요되는 시간이 길어진다.
-> 마찬가지로 페이지네이션으로 구현하면 문제를 해결할 수 있다.
결론은 무한스크롤에서 페이지네이션으로 바꾸기로 결정되었다. 무한스크롤은 다음 버전 출시 때 사용하기로 했다.