TIL3 | React - 무한스크롤 구현

hyseoseo·2021년 7월 27일
2

React

목록 보기
1/7

프리온보딩 코스 과제 1 - 무한스크롤 구현

1. Infinite Scroll이란?


많은 데이터를 하나의 페이지에 렌더링할 때 지연 로딩을 하는 방식 중 하나. 페이스북, 인스타그램 등에서 사용. 콘텐츠가 없을 때까지 계속해서 스크롤을 늘려 가면서 새로운 콘텐츠를 덧붙여서 렌더링한다.

2. Infinite Scroll 구현 방법 (without library)


1) onScroll event

사용자가 scroll을 할 때 이벤트가 발생하고, 현재 scroll 위치를 파악하여 페이지 끝에 도달했다면 새로운 콘텐츠를 요청하는 방식. scroll event는 매우 빈번하게 발생하기 때문에 이 방법을 사용하면 애플리케이션 전체의 성능이 저하될 수 있다.

2) Intersection Observer API

DOM element 간의 교차 영역에서 발생하는 변경 사항을 '비동기적으로' 감시할 수 있어서 퍼포먼스가 좋다. 익스플로러😨 이외의 브라우저들은 거의 지원한다. Intersection 외에도 자바스크립트가 지원하는 Observer API에는 MutationObserver, ResizeObserver 등이 있다.

3. Intersection Observer


Intersection Observer를 더 자세히 알아보자.

1) Observer 객체

const observer = new IntersectionObserver(callback, option {
	root: (default: null),
 	rootMargin: (default: '0px'),
	threshold: (defalut: 0) // 0.0 ~1.0 사이의 숫자나 배열 값.
})

parameter는 callback함수와 option

  • root: 관측 대상을 감싸고 있는 부모 element. default는 null (viewport)
  • rootMargin: root 요소를 감싸는 margin 값.
  • threshold: 관측 대상 element가 root와 몇 % 교차했을 때 callback 실행할지 결정하는 값. [0.0, 0.1, 0.2 ...] 식으로 배열일 경우 교차율 10%마다 callback 실행할 수 있다.
  • callback: 매개변수는 target으로 지정된 DOM element 객체 배열(entries)과 자기 자신(observer)


ex) 파란 박스가 root, 빨간 박스가 관측 대상, threshold가 0.5인 경우

2) 사용 예시 (infinite scroll 구현)

  • IntersectionObserver 등록
  • 컴포넌트 최하단에 위치한 ref를 관측 대상으로 지정
  • 관측 대상이 root와 교차하게 되면 commentData를 새로 fetch함
const InfiniteScrollList = () => {
  const [commentList, setCommentList] = useState([]);
  const [pageNumber, setPageNumber] = useState(1);
  const [isLoading, setIsLoading] = useState(true);
  const [hasMore, setHasMore] = useState(true);
  const loader = useRef(null);
  const PAGE_LIMIT = 50;

  const getCommentList = () => {
  	setIsLoading(true);
    axios
      .get(`https://.../comments?_page=${pageNumber}&_limit=10`)
      .then(response => {
        setIsLoading(false);
        setCommentList(items => [...items, ...response.data]);
        setHasMore(pageNumber !== PAGE_LIMIT);
      })
      .catch(error => console.warn(error));
  };

  useEffect(() => {
    getCommentList();
  }, [pageNumber]);

  const onIntersect = entries => {
    entries.forEach(element => {
      if (element.isIntersecting && hasMore) {
        setPageNumber(prev => prev + 1);
      }
    });
  };

  useEffect(() => {
    const options = {
      root: null,
      rootMargin: "0px",
      threshold: 0,
    };
    const observer = new IntersectionObserver(onIntersect, options);
    observer.observe(loader.current);
    return () => observer.disconnect();
  }, [loader]);

  return (
    <div className="content-container">
      {commentList.map(item => (
        <CommentCard key={item.id} item={item} />
      ))}
      <div ref={loader} className="loader">
        {isLoading && "Loading..."}
      </div>
    </div>
  );
};

export default InfiniteScrollList;

4. 문제 해결 / tips / more...


  • 처음에 setCommentList를 setCommentList([...commentList, ...response.data])로 작성했는데 get한 data가 들어가지 않았고 function으로 바꿔주니 업데이트가 제대로 되었다.

주의하겠습니다.....
  • skeleton UI와 함께 사용하는 사례가 많다. 이번 과제는 data가 text로만 이루어져 있어서 로딩 속도가 상당히 빠르므로 특별히 추가하지 않았지만- 짝님이 구현은 하셨는데 스켈레톤 UI 보여주려면 일부러 setTimeout을 걸어야 하는 상황이었 ㅋㅋ -, 추가 학습 해보는 걸로!
  • README.md도 vscode에서 작성하면 된다...! (...)
  • README 관리를 잘 하자. (구현 목록, 설치 및 시작하는 법, 배포 주소나 기능별 실행 영상 정리)

0개의 댓글