Getting - 무한스크롤

김현수·2021년 12월 2일
0

Getting-개팅

목록 보기
1/1

무한스크롤

무한스크롤이란? 페이지 처리 방식의 한 종류로써 사용자가 원하는 페이지 번호를 누르거나 입력하여 해당 페이지로 넘거어가는 것이 아닌, 페이지의 하단 끝 부분으로 스크롤할 때마다 다음 페이지를 순차적으로 불러와주는 기능

무한스크롤을 구현하기 위해서는 우선 페이지 하단까지 스크롤했다는 것을 인지할 수 있어야 합니다. 이를 인지하기 위한 방법으로는 스크롤을 할 때마다 매번 스크롤 위치를 감지하는 이벤트가 일어나게 해야합니다. 하지만 자바스크립트의 스크롤 이벤트는 조금만 움직여도 발생하기 때문에 별도의 제한 없이 이벤트를 걸어 놓으면 아주 많은 이벤트가 발생하여 리소스를 낭비하게 됩니다. 때문에 전통적으로 스크롤 이벤트를 구현할 때는 Debounce와 Throttle을 사용했습니다.

Debounce?

반복실행되는 하나의 이벤트를 묶음으로 만들어 실행을 제어하는 방식 중 하나. 특정 시간을 설정 해놓고 그 시간 안에 이벤트가 실행되면 앞서 실행된 이벤트를 모두 취소하고 시간을 초기화 한다. 즉 마지막으로 실행된 이벤트가 설정 시간 동안 갱신되지 않으면 이벤트를 비소로 실행시켜주는 방식

Throttle?

마찬가지로 동일한 이벤트의 반복실행을 제어하는 방식의 하나. 역시 원하는 시간을 설정해 놓고 그 시간을 기준으로 이벤트를 제어한다. 설정 시간 동안 일어난 동일한 이벤트를 설정 시간이 끝나는 시점에 딱 한 번만 실행시켜주는 방식. 일정시간마다 버스가 정류장에 오지만 같은 손님은 한 명만 태우는 것 정도로 이해할 수 있다.

위의 두 이벤트 제어 방식은 모두 과하게 중복실행되는 이벤트의 리소스 낭비를 막기 위해 나타난 개념들입니다. 나쁜 이벤트 제어 방식이라곤 생각하지 않지만 무한스크롤 구현함에 있어서는 조금 부족해 보였습니다.
Debounce는 무한스크롤에 적용했을 경우 사용자가 스크롤을 맨밑까지 내리고 나서 일정시간 동안 스크롤을 하지 않아야 다음 페이지가 나타날 것이기 때문에 부적합했고 Throttle은 사용자가 인지하기 힘든 정도로 시간 설정을 하면 불편함을 느끼지는 않겠지만 스크롤이 하단에 닿기 까지 몇번 정도는 불필요한 이벤트가 발생하기 때문에 가능하면 더 좋은 방식의 이벤트 제어를 원했습니다. 그러던 중 자바스크립트에서 제공해주는 InterSection Observer API를 발견했습니다

InterSection Observer API?

브라우저 뷰포트(Viewport)와 관측 대상 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인지 아닌지를 구별하는 기능을 제공해준다. 이를 이용해 다양한 이벤트를 구현할 수 있다.

InterSection Observer를 이용한 무한스크롤 구현은 Debounce나 Throttle과는 달리 비동기적으로 실행되기 때문에 메인 스레드에 무리를 주지 않습니다. 관측되는 순간 이벤트가 발생하기 때문에 보다 효율적으로 이벤트를 제어할 수 있습니다. 때문에 저는 InterSection Observer를 이용하여 무한스크롤을 구현했습니다.

InterSection Observer는 다양한 기능을 제공해주는데 저는 이중에서 무한스크롤에 필요한 isInterscting를 사용했습니다. isInterscting은 관측 대상이 뷰포트에 나타났는지를 판단해주는 InterSection Observer의 앤트리입니다.

isInterscting를 이용한 무한스크롤 구현 방식은 간단합니다. 우선 페이지 제일 하단에 관측 대상을 하나 만듭니다. 그리고 그 관측 대상이 화면에 보일 때마다 다음 페이지를 불러오는 api를 서버측에 요청하면 됩니다.

const InfinityScroll = ({ page }) => {
  const dispatch = useDispatch();
  const searchSetting = useSelector((state) => state.post.searchSetting);
  const getMoreTrigger = React.useRef();

  const getMoreObserver = new IntersectionObserver((entry) => {
    if (entry[0].isIntersecting) {
      const newSearchSetting = { ...searchSetting, page: page + 1 };
      dispatch(postActions.setSearch(newSearchSetting));
      dispatch(postActions.getMorePostMW(newSearchSetting));
    }
  });

  React.useEffect(() => {
    getMoreObserver.observe(getMoreTrigger.current);

    return () => getMoreObserver.disconnect();
  }, []);
  return <div ref={getMoreTrigger} />;
};

다만 주의해야할 점은 새로고침등의 행위로 순간적으로 페이지의 내용들이 없어져 관측 대상이 관측 된다면 한 번에 여러 개의 페이지를 불러올 수도 있다는 것입니다. 이를 방지하기 위해 값을 불러오고 있는 중인지 아닌지를 확인해주는 값을 만들어 저장해주고 이 값이 true가 아닐 때(로딩 중이 아닐 때)만 무한스크롤을 작동시켜준다면 원하지 않는 상황에서의 무한스크롤 발생을 제어할 수 있습니다

const getPostMW = (searchData) => {
  return function (dispatch, getState) {
    dispatch(setLoading(true));
    apis
      .getPots(searchData)
      .then((res) => {
        if (res.data.data.postList.length === 0) {
          ErrorAlert('해당 조건 맞는 친구들이 없습니다!');
          dispatch(setSearchPrev());
          dispatch(setLoading(false));
          return;
        }
        dispatch(getPost(res.data.data.postList));
        dispatch(setTotalPage(res.data.data.totalPages));
        dispatch(setLoading(false));
      })
      .catch((err) => {
        console.log(err);
      });
  };
};// 처음 포스트를 불러오는 미들웨어

const getMorePostMW = (searchData) => {
  return (dispatch) => {
    dispatch(setLoading(true));
    apis
      .getPots(searchData)
      .then((res) => {
        dispatch(getMorePost(res.data.data.postList));
        dispatch(setTotalPage(res.data.data.totalPages));
        dispatch(setLoading(false));
      })
      .catch((err) => {
        console.log(err);
      });
  };
}; // 포스트를 더 불러오는 미들웨어

{isLoading || totalPage <= searchSetting.page || totalPage === 1 ? (
        ''
      ) : (
        <InfinityScroll page={searchSetting.page} />
      )} // 로딩 중이거나 더 이상 불러올 페이지가 없으면 무한스크롤 컴포넌트를 화면에서 없앴다
profile
사용자에게 편안함을 주는 개발자가 되고 싶습니다

0개의 댓글