lazy loading의 무한 스크롤을 Intersection Observer API를 통해 구현하기

김예지·2022년 3월 17일
8

lazy loading이란?


웹페이지에는 많은 리소스들(이미지, 동영상 등)이 포함이 되어있다. 특히, 웹 페이지가 발전하면서 사용자에게 전송되는 리소스들의 크기가 굉장히 커졌는데, 웹페이지에 포함된 리소스들을 렌더링할때 한번에 불러오게 되면 페이지 로딩 시간이 길어질 수 밖에 없다. 특히, 이미지와 동영상과 같은 리소스들은 웹페이지 성능에 가장 많은 영향을 주고 있는 요소이기도 하기 때문에 이를 한번에 불러오면 성능상 문제가 생길 수 밖에 없을 것이다.
그래서, 리소스들을 첫번째 렌더링에서 한번에 모두 불러오지 않고 lazy loading을 통해 필요할 때 조금씩 추가해서 로딩할 수 있다.
즉, lazy loading이란 페이지가 처음 렌더링 되는 시점에서 필요하지 않는 리소스들은 최대한 미뤄서 나중에 로딩하는 기술이다.

그렇다면, lazy loading은 언제 발생시키는 것이 좋을까?
lazy loading이 발생하는 순간은 개발자가 지정할 수 있지만, 위 사진과 같이 scroll바가 가장 아래에 내려왔을 때 컨텐츠를 로드하는 경우가 일반적으로 많이 사용된다. (무한 스크롤! 스크롤이 계속 내려가면서, 컨텐츠가 새로 로딩된다.)

장점

  • 성능 향상
    웹페이지가 처음 렌더링 될 때, 모든 리소스들을 가져오면 그만큼 페이지의 로딩시간이 길어지고, 성능이 낮아진다.
    하지만, lazy loading을 통해 필요할 때마다 리소스들을 추가적으로 로딩해옴으로써 페이지 로딩 시간을 단축하고, 성능을 향상시킬 수 있다.

  • 비용 감소
    한번에 모든 리소스를 로딩하지 않고 사용자가 필요할 때만 리소스를 추가적으로 로딩해오기 때문에, 리소스 로딩에 대한 비용이 감소된다.
    무제한 데이터를 사용하는 사용자라면 보이지 않는 리소스들을 불러오는 것도 상관없지만, 나처럼 무제한이 아닌 사람들은 보이지도 않는 화면에 있는 리소드를 로딩하는데 돈이 든다면 매우 아까울 것이다 💸

  • 사용자 경험 향상
    lazy loading을 활용하여 무한 스크롤을 구현한다면, 사용자가 스크롤을 내릴때마다 컨텐츠가 새롭게 로딩되기 때문에 사용자가 웹페이지를 이탈할 확률을 낮춰줄 수 있다.
    또한, 초기 로딩 속도 향상으로 인해 로딩 체감속도가 빨라져서 사용자 경험이 향상될 수 있다.

lazy loading의 예인 무한 스크롤을 구현하기


lazy loading을 사용해서, 무한 스크롤(Infinite scroll)을 구현할 수 있다. 무한 스크롤이란, 사용자가 페이지 하단에 도달했을 때 리소스가 계속 로드되는 방식이다.

리소스가 많아 졌을 때, 위 사진과 같이 페이지 번호를 통해 다른 리소스들을 가져올 수 있는 페이지네이션 기법을 사용할 수 있다.
또 다른 방식은 무한 스크롤이다. 매번 페이지 번호를 클릭해서 리소스를 불러오는 것도 귀찮은 세상이다.
무한 스크롤을 통해, 스크롤을 할때마다 새로운 리소스를 불러올 수 있다.

그래서, lazy loading을 활용한 무한 스크롤은 어떻게 구현하면 될까?

1. scroll 이벤트 사용하기

javascript의 scroll이벤트를 사용해서 아래와 같이 스크롤바가 가장 아래에 내려왔을 때를 인식할 수 있다. (clientHeight, scrollTop, scrollHeight의 차이는 여기서!)

document.addEventListener('scroll', e => {
  console.log('scroll event 발생🔥');

  const { clientHeight, scrollTop, scrollHeight } = e.target.scrollingElement;
  if(clientHeight + scrollTop >= scrollHeight) {
    console.log('bottom');
  }
})

그런데, 이 코드의 문제점은... scroll을 내릴 때마다 이벤트가 발생한다는 것이다!
console창을 보면, 스크롤이 길지도 않는데, 355번이나 scroll 이벤트가 발생한 것을 볼 수 있다. scroll 이벤트는 사용자가 스크롤을 내릴때마다 이벤트를 발생시키기 때문에, 이와 같이 구현한다면 성능 하락을 피할 수 없다.
그래서, 만약 scroll로 무한 스크롤을 구현하고 싶은데 위와 같은 성능 하락을 피하고 싶다면 debouncethrottle를 적용해주는 것이 좋다.
debounce는 여러번 발생하는 이벤트에서 가장 마지막 또는 제일 처음 이벤트만을 실행되도록 하는 것이고,
throttle은 여러번 발생하는 이벤트를 일정 시간동안, 한번만 실행되도록 만드는 개념이다.
하지만, 우리는 Intersection Observer라는 API를 통해 debounce, throttle 없이 무한 스크롤을 구현할 수 있다!

👍 2. Intersection Observer API 사용하기

Intersection Observer(교차 관찰자)란, 내가 관찰하고 있는 요소가 사용자가 보는 화면 영역(뷰포트)에 들어왔는지를 알려주는 API이다.

사용 방법

1. 관찰자 객체를 생성한다.

let observer = new IntersectionObserver(callback, options);

여기서, callbackoptions가 들어가게 된다. callback은 필수이고, options는 선택이다.
먼저 callback에 들어가야 할 속성을 알아보자.

let callback = (entries, observer) => {
  entries.forEach(entry => {
  	// ...
  });
};
  • entries: 관찰중인 모든 대상을 담은 배열 (forEach를 통해 요소 각각에 접근 가능)
  • observer: 관찰하고 있는 관찰자 객체로, 관찰을 멈추거나 시작할 수 있다.
    • observer.unobserve(target): 관찰하던 것을 더이상 관찰하지 않도록 관찰 취소함
    • observer.observe(target): 관찰 시작

그다음으로 options에 들어가야할 속성을 알아보자.
위에서 말했다시피, options는 옵저버의 필수요소가 아니다. options객체는 callback이 호출되는 상황을 디테일하게 지정할 수 있다.

let options = {
  root: $('#list'),
  rootMargin: '0px',
  threshold: 1.0
};
  • root: 관찰 대상이 화면에 들어왔음을 감지하는 영역. 기본값은 null(viewport)이다.
  • rootMargin: 관찰 대상을 감지하는 영역을 margin까지 확장한다. css에서 margin을 설정해주는 것과 같이, 상우하좌순으로 설정해주면 된다.
  • threshold: 관찰 대상이 화면 내에 얼마나 들어왔을 때 콜백함수를 호출할지 결정한다.
    범위는 0(0%)~1(100%)이다(기본값 0).
    만약 관찰대상이 화면 내에 80% 들어왔을 때 콜백을 호출하고 싶다면 threshold값을 0.8로 주면 된다.

2. 위에서 만든 observer가 관찰할 요소를 지정한다.

const $target = document.querySelector('#target');
observer.observe($target);

여기서, 관찰할 대상인 $targetoptions를 충족할 때마다, callback이 호출된다.

3. 한번에 정리

const $list = document.querySelector('#list');
const $target = document.querySelector('#target');

let callback = (entries, observer) => {
  entries.forEach(entry => {
  	if(entry.isIntersecting) {
      observer.unobserve(entry.target);
      console.log('타겟이 화면에 노출됨');
  });
};

let options = {
  root: $('#list'),
  rootMargin: '0px',
  threshold: 1.0
};

let observer = new IntersectionObserver(callback, options);

observer.observe($target);

1) Intersection Observer 객체 observer을 생성한다. 이때, options는 선택이다.
2) observer의 관찰 대상인 Target Element를 추가한다.
3) Target Element가 threshold에서 지정한 만큼 화면에 노출되면, entries 배열에 추가하고, callback을 호출한다.
4) callback에서 전달받은 entries 배열을 확인하면서, isIntersecting(노출 여부 확인)으로 노출 여부를 확인한다.

정리


  • lazy loading은 성능향상, 비용 감소, 사용자 경험 향상의 장점들이 있기 때문에 안 쓸 이유가 없다!
  • 이러한 lazy loading의 예 중 하나가, 스크롤을 내리면 계속 새로운 컨텐츠가 로딩되는 무한 스크롤이다.
  • 무한 스크롤은 scroll 이벤트, Intersection observer API를 통해 구현할 수 있다. scroll 이벤트는 스크롤을 내릴 때마다 발생되는 단점이 있고, 이를 개선하기 위해서는 debounce, throttle를 사용해야하는 번거로움이 있었다.
    하지만, scroll 이벤트의 단점을 개선한 Intersection observer API가 등장하면서, 무한 스크롤을 구현하기가 좋아졌다.

📚 참고

profile
내가 짱이다 😎 매일 조금씩 성장하기🌱

0개의 댓글