[항해 79일 . TIL] 무한스크롤 구현

박예슬·2022년 5월 21일
0
post-custom-banner

나는 오늘

observer 무한스크롤 구현 (모임 리스트, 산 검색 리스트, 찜한산길 리스트, 나의 피드 리스트)
✔ 모임페이지 (검색기능 구현, top button 추가, 시간마감된 컨텐츠 리스트에서 제외처리, 뷰 완성)
✔ 검색페이지 (선택된 카드 스타일 변경 이벤트, 뷰 완성, 페이징 처리)
✔ 마이페이지 (피드10개 가져오기 api 연결, 수정페이지 뷰 완성)
✔ 나의 피드 페이지 (페이지 생성, 피드리스트 가져오기 api 연결)
✔ alert 모달창 추가


기억해보자

무한스크롤

무한 스크롤은 페이지 하단 영역까지 스크롤될 경우 다른 컨텐츠를 새롭게 로딩해 페이지에 추가되는 방식이다. 스크롤 액션 하나로 많은 양의 컨텐츠를 보여줄 수 있어 사용자 이탈을 막을 수 있는 장점이 있다.
리액트 환경에서의 무한 스크롤은 가상 DOM을 사용해 빠르고 부드러운 로드와 퍼포먼스를 보여주기 때문에 좋은 조합이다.
무한 스크롤 구현에 사용할 방법은 스크롤 이벤트가 아닌 기본 Web API로 제공되는 Intersection Observer를 사용하려고 한다.


Intersection Observer

Intersection Observer 의 흐름

크게 관찰자(observer) 와 관찰 대상(entry), 옵션(조건) 그리고 콜백함수(로직) 이 존재한다.

  1. 관찰자를 생성한다.
  2. 관찰 대상을 생성한다.
  3. 관찰자는 관찰 대상을 관찰한다
  4. 관찰 대상이 조건을 만족하는 상태에 놓이게 된다면 콜백 함수를 실행한다.

Intersection Observer 생성 예제

const observer = new IntersectionObserver(callback[, options]);
  • callback : 콜백함수로, 조건이 만족하면 실행할 함수
  • options : observer 콜백이 호출되는 상황을 조작할 수 있는 각종 조건들을 넣을 수 있다
    - root
    이 옵션에 정의된 Element를 기준으로 Target Element가 노출되었는지 노출 되지 않았는지를 판단한다. 기본값은 Browser Viewport이며, root 값이 null 또는 지정되지 않았을 때 기본값으로 설정된다.
    - rootMargin
    root에 정의된 Element가 가진 마진값을 의미. 사용법은 CSS의 margin 속성과 매우 유사하다. threshold를 계산할 때 rootMargin 만큼 더 계산한다.
    - threshold
    Target Element가 root에 정의된 Element에 얼만큼 노출되었을 때 Callback함수를 실행시킬지 정의하는 옵션. 즉, 관찰 대상이 화면에 어느정도 보이면 조건이 참인지를 결정하는 요소
  • observer(리턴값) : 관찰자
    - observe : 관찰 대상을 지정할 수 있다. 관찰 대상은 하나 이상이고, 조건은 변하지 않는다.
    - unobserve : 관찰 대상에 대한 관찰을 중지한다.

코드 적용

observer가 bottom을 감지하고 bottom이 true가 되면 새로운 리스트를 불러오는 방식으로 로직을 구현했다.

import React, { useState, useRef } from "react";

const Party = (props) => {
  
  const [bottom, setBottom] = useState(null);
  const bottomObserver = useRef(null);

  React.useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          setCurPage((pre) => pre + 1);
        }
      },
      { threshold: 0.25, rootMargin: "80px" }
    );
    bottomObserver.current = observer;
  }, []);

  React.useEffect(() => {
    const observer = bottomObserver.current;
    if (bottom) {
      observer.observe(bottom);
    }
    return () => {
      if (bottom) {
        observer.unobserve(bottom);
      }
    };
  }, [bottom]);
  
  return (
    <>
    	{partyList?.map((p, idx) => {
		// 모임 리스트 나열
    		...
        	return (
                // 모임 카드
            );
         })}
		...
        
    	{totalPage > curPage ? <div ref={setBottom}></div> : null}
    	...
    </>
  );
  
};


참고 링크

profile
공부중인 개발자
post-custom-banner

0개의 댓글