TIL no.53 - 무한 스크롤

김종진·2021년 4월 29일
0

React

목록 보기
12/17

무한 스크롤

화면에 데이터들을 보여줄 때는 다양한 방법이 있다. 페이스북이나 인스타그램과 같은 다양한 사이트에서는 한번에 수 많은 데이터를 보여주는 것은 비효율적이기 때문에 클라이언트의 요청 방식에 따라 무한스크롤이나 페이지네이션과 같은 방법이 있다.

무한 스크롤은 스크롤의 움직임에 따라 추가적인 데이터를 업데이트하는 방식이다.

한번 구현해보고 싶던 기능이였는데 예전에 인스타그램을 클론한 프로젝트가 있었는데 스크롤이 끝에 닿으면 다음 게시물 내용을 추가로 렌더링해주도록 무한 스크롤을 구현해보도록 했다.

구현 코드

지정한 게시물 수 만큼 보여주기

  constructor() {
    super();
    this.state = {
      feed: [],
      feedList: [],
      recommendList: [],
      items: 3,
      preItems: 0,
    };
  }

  componentDidMount() {
    fetch("data/feedData.json", {
      method: "GET",
    })
      .then((res) => res.json())
      .then((data) => {
        let result = data.slice(this.state.preItems, this.state.items);
        this.setState({
          feed: [...this.state.feed, ...result],
        });
        window.addEventListener("scroll", this.infiniteScroll, true);
      });

스크롤에 따라 게시물을 3개씩 추가적으로 업데이트 하도록 했다.

feed 배열 값에 이전 게시물들을 유지하면서 추가적인 게시물 데이터의 경우 받아오는 전체 데이터에서 slice를 활용하여 이전 게시물 수에서 3개를 추가하여 result 변수에 담아주었다.

componentDidMount가 될때마다 this.infiniteScroll 함수가 실행되도록 해주었다.

스크롤이 다다르면 infiniteScroll 작동하기

  infiniteScroll = () => {
    let scrollHeight = Math.max(
      document.documentElement.scrollHeight,
      document.body.scrollHeight
    );
    let scrollTop = Math.max(
      document.documentElement.scrollTop,
      document.body.scrollTop
    );
    let clientHeight = document.documentElement.clientHeight;

    if (scrollTop + clientHeight >= scrollHeight) {
      this.setState({
        preItems: this.state.items,
        items: this.state.items + 3,
      });
      this.componentDidMount();
    }
  };

스크롤이 어디에 위치했는지를 계산하기 위해서 document.documentElement에 접근하여야 한다.

https://javascript.info/size-and-scroll

우선 사용해야 할 값은 scrollHeight, scrollTop, clientHeight이다.

scrollHeight : 화면의 위 아래 보이지 않는 곳까지의 총 길이
scrollTop: 화면의 위 부터 클라이언트에게 보이지 않는 윗 구간
clientHeight: 현재 화면에 보여지는 높이

    let scrollHeight = Math.max(
      document.documentElement.scrollHeight,
      document.body.scrollHeight
    );
    let scrollTop = Math.max(
      document.documentElement.scrollTop,
      document.body.scrollTop
    );
    let clientHeight = document.documentElement.clientHeight;

여기서 html태그와 body태그의 총 높이를 비교하여 가장 큰 수를 구해서 담아주는 이유는 총 페이지를 핸들링할때 하나의 값만 참고해서 하는 것은 잘못된 결과를 초래할 수 있기 때문이다.
clientHeight의 경우는 현재 보여지는 화면이기 때문에 html에 접근해서 구한다.

스크롤에 끝에 닿아 게시물이 밑으로 추가적으로 렌더링 되면 총 스크롤 높이의 길이가 달라지기 때문에 이를 다시 계산하여 판단해주는 조건문이 필요하다.

if (scrollTop + clientHeight >= scrollHeight)

화면의 맨 위(scrollTop)부터 게시물의 높이(clientHeight이다)를 더한 값이 화면의 총 높이(scrollHeight) 보다 클 경우 infiniteScroll 함수가 실행되도록 하였다.

      this.setState({
        preItems: this.state.items,
        items: this.state.items + 3,
      });

이전 게시물 갯수 preItems를 현재 게시물 수로 업데이트하고 추가로 불러올 게시물 수 3개를 더해준다.

componentDidMount가 될때 window.addEventListener의 Scroll시에 infiniteScroll 함수를 실행시킨다.

구현 화면


구웃~ ㅎㅅㅎ!

Tip (추가 작성)

document 속성을 활용하여 한 높이 측정은 예상치 못한 오차가 발생하여 원하는 스크롤 기능 구현을 못할 사이드이펙트가 있다.

Throttling

!! Scroll 이벤트는 스크롤을 움직일 때마다 이벤트가 발생하기 때문에 성능 문제를 야기 할 수 있다..!

이를 해결하기 위해 스크롤 이벤트에 쓰로틀링(throttling)을 적용하여 이벤트를 제한 할 수 있다.

: 이벤트를 일정한 주기마다 발생하는 기술. 마지막 함수가 호출된 후 일정 시간이 지나기 전엔 다시 호출되지 않도록 하는

Debounce

이벤트를 그룹핑해서 특정 시간이 지난 후 하나의 이벤트만 발생하도록 하는 기술. 연달아서 호출되는 함수들 중 마지막 함수만 호출하도록 하는 것

도움을 준 멋진 사이트! 감사합니다.
https://medium.com/@ghur2002/react%EC%97%90%EC%84%9C-infinite-scroll-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-128d64ea24b5

profile
FE Developer

0개의 댓글