인피니티 스크롤 구현하기

채희태·2022년 10월 17일

인피니티 스크롤 ?

페이지 하단에 도달하면 추가로 데이터를 페치하여 한 화면에 데이터를 추가로 로드하여 렌더링 하는 방식이다.
sns-app 프로젝트를 이와 같은 방식으로 구현하였다.

  • scrollHeight는 화면에 보이지 않는 높이도 포함된 페이지의 총 높이이다.
  • scrollTop은 이미 스크롤되어서 보이지 않는 구간의 높이이다.
  • clientHeight는 클라이언트, 즉 사용자에게 보여지는 페이지의 높이이다.

즉, 이미 스크롤 된 페이지 높이인 scrollTop과 사용자에게 보여지는 페이지 높이인 clientHeight가 페이지의 총 높이인 scrollHeight와 같거나 크면 페이지의 끝에 도달한 것이다.
scrollTop + clientHeight >= scrollHeight
이 방법을 이용하여 구현해 보았다.

초기 데이터 페치

우선 페이지에 처음 진입했을때 데이터를 페치해 준다.

react 사용시

useEffect(() => {
   axios.post("http://localhost:7000/api/post/getPost").then((res) => {
     if (res.data.success) {
       dispatch({ type: ADD_POST, payload: res.data.doc });
     } else {
       console.log(res.data.err);
     }
   });
}, []);

next 사용시

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req }) => {
      try {
        const res = await axios.post("http://localhost:7000/api/post/getPost");
        const payload = await res.data.doc;
        store.dispatch({ type: ADD_POST, payload });
      } catch (err) {
        console.log(err);
      }
    }
);

인피니티 스크롤을 구현해 추가 데이터 페치

//이미 페치한 데이터는 스킵
const [skip, setSkip] = useState(8);
//로딩창 구현
const [isFetching, setIsFetching] = useState(false);

//추가 데이터를 페치하는 함수
const fetchMorePosts = async () => {
  //로딩창 띄우기
  await setIsFetching(true);
  //axios로 추가 데이터 페치
  await axios
    .post("http://localhost:7000/api/post/getPost", { skip })
    .then((res) => {
      if (res.data.success) {
        dispatch({ type: ADD_POST, payload: res.data.doc });
        setSkip(skip + 8);
      } else {
        console.log(res.data.err);
      }
    });

  //페칭이 완료되었으므로 로딩창 닫기
  setIsFetching(false);
};

//scroll event handler 생성
const handleScroll = () => {
  const scrollHeight = document.documentElement.scrollHeight;
  const scrollTop = document.documentElement.scrollTop;
  const clientHeight = document.documentElement.clientHeight;
  if (scrollTop + clientHeight >= scrollHeight && isFetching === false) {
    //페이지 끝에 도달했으므로 페칭
    fetchMorePosts();
  }
};

//scroll event listener 생성
useEffect(() => {
  if (posts.length % 8 === 0) {
    // handleScroll event listener 등록
    window.addEventListener("scroll", handleScroll);
    return () => {
      // handleScroll event listener 해제
      window.removeEventListener("scroll", handleScroll);
    };
  }
});

스크롤 이벤트를 감지하는 event listener를 등록하고, 스크롤 끝에 닿았을 때 추가 데이터를 페치하는 함수를 실행한다.

추가 데이터를 페치하는 함수를 실행하면 isFetching을 이용해 로딩창이 뜨도록 하였다.

데이터는 8로 나누어 떨어지도록 구현하였으므로 8로 나누어 떨어지지 않으면 추가할 데이터가 없는 걸로 간주하여 scroll event listener를 실행하지 않았다.

const post = (state = initialState, action) => {
  switch (action.type) {
    case ADD_POST:
      return [...state, ...action.payload];
    case DELETE_POST:
      const deletedPosts = state.filter((post) => post._id !== action.payload);
      return [...deletedPosts];
    default:
      return state;
  }
};

리듀서에서는 추가 데이터가 밑에서 로딩되도록 하였다.

profile
기록, 공부, 활용

0개의 댓글