RestAPI + 무한스크롤

박정호·2023년 1월 15일
0

API

목록 보기
4/6
post-thumbnail

🚀 Start

데이터가 기본적으로 스크롤하여 출력되야하는 정도의 양을 가진 서비스라면 대부분 무한스크롤을 사용하여 그때마다의 데이터만 출력되게하여 성능을 최적화시키는 것 같다.


이미 프로젝트들을 진행해보며 세가지 방법으로 무한 스크롤을 구현해봤다.

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

👉 PJH's Shopping Mall - 무한스크롤


이번엔 SWR, React Query 없이 아주~ 기본적인 API 통신으로 무한스크롤을 구현해보자.



🖥 Client

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} />
    </>
  );
};


⚙️ useInfiniteScroll

무한스크롤 기능을 동작하는 커스텀훅이다.

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;


💾 Server

 {
    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));
    },
  },
profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글