[React] scroll Event 무한스크롤

DaYoung·2023년 10월 29일
0

React

목록 보기
1/6

복지몰 프로젝트를 하면서 상품 리스트를 Scroll Event를 이용하여 무한스크롤을 구현하였다.

Scroll Event

콘텐츠 전체 길이와 현재 스크롤 길이를 비교하여 스크롤 바닥을 감지하는 방법이다.
이 방식은 스크롤이 움직일 때마다 이벤트 핸들러가 호출되므로 반드시 Throttle이나 Debounce를 사용해서 이벤트를 빈도를 줄여줘야한다.

const 복지몰: React.FunctionComponent = () => {
  const [visibleItems, setVisibleItems] = useState<number>(ITEMS_PER_PAGE); // 페이지당 보여줄 항목 수
  const [isLoading, setIsLoading] = useState<boolean>(false);

  // 스크롤 이벤트 핸들러
  const handleScroll = () => {
    if (isLoading) return;

    const scrollHeight = document.documentElement.scrollHeight;
    const currentHeight =
      document.documentElement.scrollTop + window.innerHeight;

    if (currentHeight + window.innerHeight >= scrollHeight - 140) {
      setIsLoading(true);
      setTimeout(() => {
        setVisibleItems((pre) => pre + ITEMS_PER_PAGE);
        setIsLoading(false);
      }, 1000);
    }
  };

  const debouncedHandleScroll = debounce(handleScroll, 100);

  useEffect(() => {
    window.addEventListener("scroll", debouncedHandleScroll);
    return () => {
      window.removeEventListener("scroll", debouncedHandleScroll); // 이전에 등록한 스크롤 이벤트 리스너 제거
    };
  }, [debouncedHandleScroll]);

  return (
    <Container>
      <Title>오하우스 복지몰</Title>
      <SubTitle>
        오하우스 모든 직원들이 복지포인트를 이용하여
        <br />
        쇼핑을 할 수 있는 공간입니다
      </SubTitle>
      <ProductCardContainer>
        {DummyData.slice(0, visibleItems).map(
          (data: ProductModel.IProductModel, index: number) => {
            return <복지몰_상품 key={data.id} data={data} index={index} />;
          }
        )}
      </ProductCardContainer>
      {isLoading && (
        <>
          {visibleItems >= DummyData.length 
		  ? null 
		  : ( <div style={{ textAlign: "center", margin: "10px 0" }}>
              <SyncLoader color={Color.Orange} />
            </div>
          )}
        </>
      )}
    </Container>
  );
};

ScrollHeight: 보이지 않는 화면까지 포함하여 전체 글의 길이
ScrollTop: 맨 처음부터 ~ 현재 화면에 보이는 부분까지의 길이
innerHeight: 창 틀을 뺀 화면의 높이

    if (currentHeight + window.innerHeight >= scrollHeight - 140) {
      setIsLoading(true);
      setTimeout(() => {
        setVisibleItems((pre) => pre + ITEMS_PER_PAGE);
        setIsLoading(false);
      }, 1000);
    }

1) "currentHeight + window.innerHeight >= scrollHeight - 140"
현재 스크롤 위치와 전체 콘텐츠 높이를 비교하여 페이지 하단에 도달했다고 판단되어 if문을 실행한다.

2) setTimeout 수를 사용하여 1초 후에 코드를 실행하게 하였고
그 시간 동안 로딩 상태가 표시된다.

3) "setVisibleItems(visibleItems + ITEMS_PER_PAGE)"
새로운 콘텐츠를 화면에 보여주기 위해서 항목수를 증가 시키는데
상수로 6을 정의해서 6개씩 추가로 보여준다.

4) 마지막으로 콘텐츠를 성공적으로 로드하면 로딩상태를 false로 해제시켜준다.

   const debouncedHandleScroll = debounce(handleScroll, 100);

  useEffect(() => {
    window.addEventListener("scroll", debouncedHandleScroll);
    return () => {
      window.removeEventListener("scroll", debouncedHandleScroll); 
      // 이전에 등록한 스크롤 이벤트 리스너 제거
    };
  }, [debouncedHandleScroll]);

1) "const debouncedHandleScroll = debounce(handleScroll, 100)" debounce는 연속적으로 호출되는 함수들 중 마지막 함수만 호출하도록 하는 것으로 scorll 할때마다 계속적으로 호출하는걸 방지하고 0.1초 후에 주어진 handleScroll 함수를 실행한다.

2) useEffect를 사용하여 visibleItems의 상태 변수가 변할 때마다 실행되는데 스크롤 이벤트 리스너를 등록하고, 컴포넌트가 언마운트 될 때는 이벤트 리스너를 제거해준다.


<참고>

페이지네이션

페이지네이션을 사용하면 사용자가 특정 페이지로 이동하여 원하는 데이터를 빠르게 찾을 수 있어 사용자의 편의성을 향상시켜준다.
보통 페이지네이션은 어드민, 게시판에서 많이 사용 하였다.

무한스크롤

무한 스크롤은 사용자가 페이지 끝에 도달했을 때 새로운 항목이 자동으로 로드되어 추가 스크롤 없이도 계속해서 컨텐츠를 볼 수 있다.
사용자는 페이지를 새로고침하거나 페이지 클릭 없이도 이동할 수 있으니 더 편리하다고 볼 수 있다.
또한 새로운 컨텐츠가 계속해서 로드되기 때문에 더 많은 제품을 보고 구매를 할 수 있다.
보통 사용자들이 페이지에 많이 머무는 사이트에서 무한스크롤을 많이 사용하는 것 같다.

debounce, throttle 차이점

debounce: 해당 이벤트 그룹이 일시정지 되었다고 판단되는 시점이 지나면 최초 또는 최후의 이벤트에 대해서만 처리하는 방식

ex) 입력이 끝난 후 api를 호출한다.

Throttle: 이벤트를 일정 주기마다 처리함으로써 이벤트를 제어하는 방식이다. Throttle의 설정시간으로 1ms를 주게 되면 해당 이벤트는 1ms 동안 최대 한 번만 발생하게 된다.

ex) 500ms마다 api를 호출한다.

profile
안녕하세요. 프론트앤드 개발자 홍다영입니다.

0개의 댓글