Projects

[Namoldak] Intersection Observer API 를 사용한 무한스크롤

soobin Choi 2023. 2. 24. 12:19

React 로 무한스크롤 구현하기! Intersection Observer API 를 사용하는 방법?

Intersetction Observer API란?

Intersection Observer API는 뷰포트 내 요소의 교차(intersection) 상태를 관찰하는 API입니다. 뷰포트는 웹 페이지에서 보이는 영역을 말하며, Intersection Observer API를 사용하면 이 영역 내에서 요소들이 어떤 상태인지를 비동기적으로 관찰할 수 있습니다.

Intersection Observer API는 크로스 브라우징을 지원하며, 모바일 기기에서도 정상적으로 작동합니다. 이 API를 이용하여 웹 페이지에서 무한 스크롤, 광고 배너 로딩, 이미지 지연 로딩 등 다양한 용도로 활용할 수 있습니다.

Intersection Observer API는 다음과 같은 메서드와 프로퍼티를 제공합니다.

  • IntersectionObserver 생성자: IntersectionObserver 객체를 생성합니다.
  • IntersectionObserver.observe(targetElement): 지정한 요소를 IntersectionObserver에 등록하여 교차 상태를 관찰합니다.
  • IntersectionObserver.disconnect(): IntersectionObserver에서 등록한 모든 요소의 교차 관찰을 중지합니다.
  • IntersectionObserver.unobserve(targetElement): 지정한 요소의 교차 관찰을 중지합니다.
  • IntersectionObserverEntry.intersectionRatio: 교차 영역의 비율을 반환합니다.
  • IntersectionObserverEntry.isIntersecting: 요소와 뷰포트가 교차하는지 여부를 반환합니다.

IntersectionObserver 생성자는 다음과 같은 옵션을 제공합니다.

  • root: 교차를 관찰할 요소의 부모 요소를 지정합니다. 기본값은 브라우저의 뷰포트입니다.
  • rootMargin: 교차 영역을 계산할 때, 지정한 마진 값을 고려합니다.
  • threshold: 교차 영역의 비율이 지정한 값 이상일 때, 콜백 함수를 호출합니다. 기본값은 0입니다.

무한스크롤 구현 코드

초기값 선언과 함수 부분

    // initial state
  const [comments, setComments] = useState([]);
  const [totalPage, setTotalPage] = useState(0);
  const [isLoading, setIsLoading] = useState(false);
  const [commentPage, setCommentPage] = useState(0); // 스크롤이 닿았을 때 새롭게 데이터 페이지를 바꿀 state
  const pageEnd = useRef(); // 페이지의 마지막 요소(infinite scroll의 탐색 타겟)

  const loadMore = () => {
    if (commentPage < totalPage) {
      setCommentPage((commentPage) => commentPage + 1);
    }
    if (commentPage === totalPage - 1) {
      setIsLoading(false);
    }
  };
  
  useEffect(() => {
    if (isLoading) {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            loadMore();
          }
        },
        { threshold: 1 },
      );
      // 옵져버 탐색 시작
      observer.observe(pageEnd.current);
    }
  }, [isLoading]);

return문

        <div>
		(생략)
          {comments?.map((i) => {
            return (
              <div key={i.id}>
                <Comment
                  commentId={i.id}
                  comment={i.comment}
                  nickname={i.nickname}
                  createdAt={i.createdAt}
                  comments={comments}
                  setComments={setComments}
                />
              </div>
            );
          })}
          <Target ref={pageEnd} />
        </div>

깃허브

https://github.com/namoldak/Frontend/blob/main/src/components/Community/Post/PostDetail.jsx

React 에서 Intersection Observer API를 사용하지 않고 무한스크롤을 구현하는 방법은?

1. 스크롤 이벤트와 DOM 높이 비교

무한 스크롤을 구현하는 가장 간단한 방법 중 하나는 스크롤 이벤트를 사용하여 DOM의 높이와 스크롤 위치를 비교하는 것입니다.

import React, { useState, useEffect } from 'react';

function InfiniteScroll() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  
  useEffect(() => {
    // 초기 데이터 로드
    fetchData();
  }, []);
  
  useEffect(() => {
    // 스크롤 이벤트 리스너 등록
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
  
  const handleScroll = () => {
    // 스크롤이 바닥에 닿았을 때 다음 페이지 데이터 로드
    if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight) {
      fetchData();
    }
  };
  
  const fetchData = async () => {
    // API에서 데이터를 가져와서 data state에 추가
    const response = await fetch(`https://example.com/api?page=${page}`);
    const newData = await response.json();
    setData((prevData) => [...prevData, ...newData]);
    setPage((prevPage) => prevPage + 1);
  };

  return (
    <ul>
      {data.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

export default InfiniteScroll;

위 코드에서 handleScroll 함수는 스크롤 이벤트가 발생할 때마다 호출됩니다. 이 함수는 현재 스크롤 위치와 DOM 높이를 비교하여 스크롤이 바닥에 닿았는지 확인합니다. 스크롤이 바닥에 닿으면 fetchData 함수를 호출하여 다음 페이지 데이터를 가져와서 data state에 추가합니다.

2. button 클릭으로 추가 데이터 가져오기

또 다른 방법은 무한 스크롤 대신 "더 보기" 버튼을 클릭하여 추가 데이터를 가져오는 것입니다.

import React, { useState, useEffect } from 'react';

function InfiniteScroll() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  
  useEffect(() => {
    // 초기 데이터 로드
    fetchData();
  }, []);
  
  const handleLoadMore = () => {
    // "더 보기" 버튼 클릭 시 다음 페이지 데이터 로드
    fetchData();
    setPage((prevPage) => prevPage + 1);
  };
  
  const fetchData = async () => {
    // API에서 데이터를 가져와서 data state에 추가
    const response = await fetch(`https://example.com/api?page=${page}`);
    const newData = await response.json();
    setData((prev

React 를 쓰지 않고 무한스크롤을 구현하는 방법은?

React를 사용하지 않고 무한 스크롤을 구현하는 방법에는 일반적으로 두 가지 방법이 있습니다.

  1. 스크롤 이벤트와 DOM 높이 비교
    • 스크롤 이벤트를 사용하여 DOM의 높이와 스크롤 위치를 비교하는 방법입니다.
    • 스크롤 위치와 DOM 높이를 비교하여 스크롤이 바닥에 닿았는지 확인하고, 바닥에 닿았다면 다음 페이지 데이터를 가져와서 페이지에 추가합니다.
  2. AJAX 요청과 데이터 렌더링
    • AJAX 요청을 사용하여 페이지당 일정한 개수의 데이터를 가져오는 방법입니다.
    • 초기 페이지 로드 시 일정한 개수의 데이터를 가져오고, 스크롤이 아래쪽에 가까워질 때마다 추가 데이터를 가져와서 페이지에 추가합니다.

두 방법 모두 JavaScript와 HTML을 사용하여 구현할 수 있습니다. 예를 들어, 첫 번째 방법의 경우 다음과 같은 코드를 사용할 수 있습니다.

// 스크롤 이벤트 리스너 등록
window.addEventListener('scroll', handleScroll);

// 스크롤 이벤트 핸들러
function handleScroll() {
  // 스크롤이 바닥에 닿았는지 확인
  if (window.innerHeight + window.scrollY === document.body.offsetHeight) {
    // 다음 페이지 데이터 가져오기
    const data = getNextPageData();
    // 가져온 데이터를 페이지에 추가하기
    renderData(data);
  }
}

위 코드에서 window.addEventListener 함수를 사용하여 스크롤 이벤트 리스너를 등록하고, handleScroll 함수에서 스크롤이 바닥에 닿았는지 확인합니다. 바닥에 닿았을 때 getNextPageData 함수를 호출하여 다음 페이지 데이터를 가져오고, renderData 함수를 호출하여 가져온 데이터를 페이지에 추가합니다. 이때 renderData 함수는 가져온 데이터를 HTML 요소로 변환하여 페이지에 추가하는 역할을 합니다