무한 스크롤 만들기 : Throttling

nnm·2020년 3월 31일
18
post-thumbnail

프로그래머스 2020 Dev-Matching : 웹 프론트엔드 과제 복기
https://github.com/woohyeonjo/ilovecat

페이징 UI무한 스크롤 UI는 많은 컨텐츠를 효과적으로 보여주는 대표적인 UI다. 두 UI는 각각 장단점이 존재하는데 정말 단순하게 유저의 접속 기기만 따진다면 PC에는 페이징 UI가 모바일에는 무한 스크롤 UI가 좀 더 어울린다. 하지만 최근에는 반응형 페이지를 통해서 대표적인 스크린 사이즈를 모두 커버하는 경우가 많고 모바일 트래픽이 더 큰 경향이 있기 때문인지 무한 스크롤 UI가 더 흔하게 보이는 것 같다. 가장 중요한 것은 서비스와 보여주려는 컨텐츠의 특성을 잘 파악해서 적합한 UI를 선택하는 것이다.

무한 스크롤을 구현해보자

스크롤 바가 마지막 컨텐츠를 보여주는 시점에 추가적인 데이터를 제공하는 것이다. 여러가지 사이즈를 이용해서 마지막 컨텐츠가 보이는 시점을 구할 수 있다.

전체 문서의 높이에서 현재 보이는 영역의 높이를 뺀 결과는 총 스크롤 할 수 있는 높이다. 따라서 스크롤 된 높이가 이와 같다면 스크롤을 끝까지 완료한 것이며 마지막 컨텐츠가 보이는 시점이 될 것이다.

// scrollFetch.js

  function scrollFetch(fetchData) {
      window.addEventListener('scroll', () => {
          if (getScrollTop() < getDocumentHeight() - window.innerHeight) return;
          fetchData();
      });
  }
  // 현재 스크롤한 높이를 구하는 함수 
  function getScrollTop() {
      return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
  }
  // 문서의 높이를 구하는 함수
  function getDocumentHeight() {
      const body = document.body;
      const html = document.documentElement;
	  
      return Math.max(
          body.scrollHeight, body.offsetHeight,
          html.clientHeight, html.scrollHeight, html.offsetHeight
      );
  }

  export { scrollFetch };

데이터를 추가로 받아오는 시점을 스크롤 이벤트로 멋지게 구현했다. 근데 문제가 있다. 스크롤을 움직일 때 마다 함수가 실행되는 것이다. 스크롤이 마지막이 아니면 데이터가 추가되지 않지만 그래도 계속해서 스크롤이 마지막인지 확인하는 비교를 수행하다보니 성능문제가 있다.

Throttling을 적용하자

이 문제에 해결법으로 적용가능한 프로그래밍 기법이 두 가지 있다.

일반적으로 Debouncing은 검색에서 사용하고 Throttling은 스크롤에 사용된다고 한다. 아무래도 검색은 유저가 검색어 타이핑을 완료하는 시점에 함수가 실행되어야 하니 Debouncing이 사용되고 스크롤의 경우에는 변화하는 길이 만큼의 횟수로 스크롤 이벤트가 발생하니 일정 시간으로 함수 호출을 막는 Throttling이 적당해서인 것 같다.

Throttling 구현은 이 글로 공부하였다.

  //throttle.js
  export const throttling = () => {
      let throttleCheck;

      return {
          throttle(callback, milliseconds){
              if(!throttleCheck){
                  throttleCheck = setTimeout(() => {
                      callback(...arguments);
                      throttleCheck = false;
                  }, milliseconds);
              }
          }
      };
  };
  • 클로저 구조를 가지고 있다. throttleCheck를 외부에서 접근하지 못하도록 하기 위해서 사용한거 같다. 사실 아직 클로저에 대해서 정확히 이해하지 못해서 따로 공부를 해야겠다.
  • 작동방식
    • throttleCheck를 통해서 throttle 상태가 아닐 때만 callback을 실행한다.
    • setTimeouttimer id를 반환하기 때문에 호출 시점에 throttleCheck에 숫자가 할당되어 true 값으로 여겨진다.
    • callback의 실행이 완료되고 throttleCheckfalse로 바꾸어 다시 함수 호출이 가능한 상태로 바꾼다.
	// scrollFetch.js
  import { throttling } from './throttle.js';

  const throttler = throttling();

  function scrollFetch(fetchData) {
      window.addEventListener('scroll', () => {
          throttler.throttle(() => {
              console.log("Activate Scroll Event");
              if (getScrollTop() < getDocumentHeight() - window.innerHeight) return;
              fetchData();
          }, 700);
      });
  }
  ...
  • 기존에 스크롤 이벤트 안에서 실행되던 부분에 Throttling을 적용한다.
  • 네이밍이 너무 마음에 안들지만 떠오르지 않는다...

Throttling을 쓸 필요가 없다?

Throttling을 이용한 스크롤 이벤트 개선을 공부하던 중 이전에 Lazy Loading을 구현할 때 사용하였던 Intersection Observer를 이용하면 더 간단하게 무한 스크롤을 구현할 수 있다는 사실을 알게되었다. 다음에는 Intersection Observer를 이용해서 구현해봐야겠다. 그래도 Throttling, Debouncing은 무한 스크롤 뿐만 아니라 적용할 수 있는 곳이 많아서 좋은 공부가 되었다.

profile
그냥 개발자

0개의 댓글