IntersectionObserver와 debounce 방식

리액트에서 무한스크롤은 IntersectionObserver api를 사용해서 구현할 수도 있고, debounce 기반 스크롤 이벤트 감지로 구현할 수도 있다.

IntersectionObserver api 사용 방식

1단계: 상태 및 ref 준비

const [items, setItems] = useState<YourItemType[]>([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const containerRef = useRef<HTMLDivElement | null>(null); // 하단 감지용 ref


2단계: 데이터 패칭 함수 만들기

const fetchNextPage = async () => {
  setIsLoading(true);
  try {
    const res = await getNextPageResults(page); // 예: `/api/items?page=${page}`
    setItems((prev) => [...prev, ...res.items]);
    setPage((prev) => prev + 1);
  } catch (err) {
    console.error("데이터 로딩 실패", err);
  } finally {
    setIsLoading(false);
  }
};


3단계: IntersectionObserver 설정

useEffect(() => {
	if (!containerRef.current) return;
	
  const observer = new IntersectionObserver(
    (entries) => {
      if (entries[0].isIntersecting && !isLoading) { // isLoading: 중복 호출 방지
        fetchNextPage();
      }
    },
    { threshold: 1 } // 요소가 화면에 완전히 들어왔을 때 감지
  );
	observer.observe(containerRef.current);

  return () => {
    observer.disconnect();
  };
}, [containerRef, items]);


4단계: ref를 리스트 마지막 요소 아래에 배치

<div className="item-list">
  {items.map((item, index) => (
	  <Fragment key={index}>
	    <ItemCard item={item} />
	    {index === items.length - 1 && 
		    (<div ref={containerRef} className="h-10" />)} {/* 하단 감지용 */}
		</Fragment>
  ))}
</div>


5단계: 로딩 UI 추가 (선택)

{isLoading && <div className="text-center text-sm py-4">Loading more...</div>}



debounce 기반 스크롤 이벤트 감지 방식

let debounceTimer = null
const onScroll = () => {
  if (debounceTimer) clearTimeout(debounceTimer) // 너무 스크롤 이벤트 많이 발생하는 것 방지
  debounceTimer = setTimeout(() => {
    console.log('scroll')
    const nearBottom =
      window.innerHeight + window.scrollY + 100 > document.documentElement.offsetHeight
    if (nearBottom) {
      movieStore.getMovieMore(++page, type, keyword)
    }
  }, 200)
}

useEffect(()=>{
	window.addEventListener('scroll', onScroll)
}, [])




Categories:

Updated:

Leave a comment