데이터가 기본적으로 스크롤하여 출력되야하는 정도의 양을 가진 서비스라면 대부분 무한스크롤을 사용하여 그때마다의 데이터만 출력되게하여 성능을 최적화시키는 것 같다.
✅ 이미 프로젝트들을 진행해보며 세가지 방법으로 무한 스크롤을 구현해봤다.
1️⃣ REST API
+ useSWRInfinite
+ IntersectionObserver
👉 PJH's Community Site - Infinite Scroll
2️⃣ REST API
+ useInfiniteQuery
+ React-infinite-scroller
👉 React Query - useInfiniteQuery
3️⃣ GraphQL
+ useInfiniteQuery
+ IntersectionObserver
이번엔 SWR, React Query 없이 아주~ 기본적인 API 통신으로 무한스크롤을 구현해보자.
import useInfiniteScroll from "../hooks/useInfiniteScroll";
const MsgList = () => {
const [hasNext, setHasNext] = useState(true);
const fetchMoreEl = useRef(null);
const intersecting = useInfiniteScroll(fetchMoreEl);
...
const getMessages = async () => {
const newMsgs = await fetcher("get", "/messages", {
params: { cursor: msgs[msgs.length - 1]?.id || "" },
});
if (newMsgs.length === 0) {
setHasNext(false);
return;
}
setMsgs((msgs) => [...msgs, ...newMsgs]);
};
useEffect(() => {
if (intersecting && hasNext) getMessages();
}, [intersecting]);
return (
<>
...
<div ref={fetchMoreEl} />
</>
);
};
무한스크롤 기능을 동작하는 커스텀훅이다.
intersectionObserver
라는 API를 사용하여 페이지가 넘어가는 교차부분을 관찰하여 때에 따른 데이터를 출력시킬 수 있다. (참고)
const useInfiniteScroll = (targetEl) => {
const observerRef = useRef(null);
const [intersecting, setIntersecting] = useState(false);
const getObserver = useCallback(() => {
if (!observerRef.current) {
observerRef.current = new IntersectionObserver((entries) =>
setIntersecting(entries.some((entry) => entry.isIntersecting))
);
}
return observerRef.current;
}, [observerRef.current]);
useEffect(() => {
if (targetEl.current) getObserver().observe(targetEl.current);
return () => {
getObserver().disconnect();
};
}, [targetEl.current]);
return intersecting;
};
export default useInfiniteScroll;
{
method: "get",
route: "/messages",
handler: ({ query: { cursor = "" } }, res) => {
const msgs = getMsgs();
const fromIndex = msgs.findIndex((msg) => msg.id === cursor) + 1;
res.send(msgs.slice(fromIndex, fromIndex + 15));
},
},