[TIL] Lazy Loading

심지훈·2021년 8월 18일
0

TIL

목록 보기
1/5

Lazy Loading

레이지 로딩이란?

레이지 로딩이란 웹사이트에서 DOM이 로드되고 나서 컨텐츠를 불러 올때 이미지와 같이 용량이 큰 파일들을 일부러 늦게 로드하는 기법을 말한다. 이미지가 적다면 크게 문제가되진 않겠지만 수십개 또는 수백개라면 레이지 로딩을 하지 않고서는 수백개를 다 불러들어와야만 사용자에게 화면이 보이지만 레이지 로딩 기법을 사용하면 화면에 보일 이미지들만 로드한 뒤 스크롤,리사이즈 등의 이벤트가 발생하면 다른 이미지들을 보여질 부분만 차례대로 로드하는 기법이다.

구현

Intersection Observer

IE를 제외하고 대부분의 메이저 브라우저에서는 Intersection Observer를 지원한다.

Intersection Observer란 쉽게말해서 어느 요소를 관찰하는 객체이다.

A라는 객체를 만들어서 " 야 너 저기가서 클래스가 '벨로그'인 img 태그를 가진애들 뭐하는지 한번 봐봐 " 라고 시키는것이다.

그리고 우리 ( 개발자들 )은 "A야 보기만 하지말고 아까 걔네들 오는지 안오는지 보다가 집에오면 callback이란 심부름 좀 시켜~"라고 말하는것이다.

코드로 보면

// 지켜 볼 애들을 찾고
const 지켜 볼 애들 = document.querySelectorAll('.벨로그');

// IntersectionObserver를 선언하고
// 관찰만 하지말고 callback이란 일을 시키도록 미리 정의하고
const io = new IntersectionObserver(callback,options);

// 애들을 지켜보기 시작.
(지켜 볼 애들).forEach((한 아이) => {
  io.observe(아이);
});

IntersectionObserver( 이하 IO)를 이용하면 요소가 화면에 나올때와 화면에서 사라질때를 알 수 있다. 꼭 화면 ( window)일 필요는 없고
요소를 포함하고 있는 컨테이너 역할을 하는 요소여도 된다.

레이지로딩은 IO를 이용하여 이렇게 화면에 나타나는 순간을 감지하고 이때 이미지를 로드한다.

그렇다면 어떻게 그 순간 이미지를 로드할까?

이미지를 가져 올 경로는 이미지 태그의 src에 넣는데 우선 이 속성을 비워둔다 그러면 아래와 같이 빈 이미지 형태가 된다.

( img 태그를 사용할때 왠만하면 alt 속성과 width height 속성을 미리 지정해주자 그러면 브라우저가 해당 요소를 렌더링할때 아래와 같이 이미지의 크기를 미리 정해주기 때문에 이미지가 다 로드 되더라도 다른 요소를 리렌더링 할 필요가 없으므로 렌더링 성능이 향상된다. )

코드로 보자면 아래와 같다.

img src를 비워두는 대신에 HTML data- 사용자 정의 속성을 이용하여 여기에다가 이미지의 경로를 담아두다가 화면에 나타나는 순간
data-
속성에 정의된 이미지 경로를 img src에 쏙 넣어준다.

const images = document.querySelectorAll(".lazy");
  if ("IntersectionObserver" in window) {
    const options = {
      root: document.querySelector(".container"),
      rootMargin: "0px",
      threshold: 0.5,
    };
    const io = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          img.classList.remove("lazy");
          io.unobserve(img);
        }
      });
    }, options);

    images.forEach((img) => {
      io.observe(img);
    });

코드에서 option 값에 threshold 값을 0.5로 주었는데 이 뜻은 화면에 요소가 반이상 보일때만 이미지를 로드하겠다는 뜻이다.

아래를 확인 해 볼 수 있다.

Intersection Observer가 없는 경우

이럴때에는 이미지의 경로를 HTML data-* 속성에 넣어주는것은 같지만 부모 요소로부터 이미지의 y좌표와 윈도우의 위치를 이용해서 화면에 보여질 타이밍을 계산해서 로드한다.

핵심적인 코드만 보자면 아래와 같다.

lazyloadTimeOut = setTimeout(() => {
        let scrollTop = window.pageYOffset;
        images.forEach((img) => {
          if (img.offsetTop < window.innerHeight + scrollTop) {
            img.src = img.dataset.src;
            img.classList.remove("lazy");
          }

window.pageYOffset을 통해 얼마나 스크롤 했는지 알 수 있고 window.innerHeight을 통해 윈도우의 높이 값을 알 수 있다.
이미지의 Y좌표의 위치가 현재 화면에 보여지는 영역에 들어 왔을때
if( img.offsetTop < window.innerHeight + scrollTop) 이미지를 로드한다.

그리고 해당 로드 작업을 setTimeout함수를 이용해 비동기적으로 실행한다.

전체코드를 보자면 아래와 같다.

const lazyload = () => {
      let lazyloadTimeOut = null;

      if (lazyloadTimeOut) {
        clearTimeout(lazyloadTimeOut);
      }

      lazyloadTimeOut = setTimeout(() => {
        let scrollTop = window.pageYOffset;
        images.forEach((img) => {
          if (img.offsetTop < window.innerHeight + scrollTop) {
            img.src = img.dataset.src;
            img.classList.remove("lazy");
          }
        });
        if (images.length === 0) {
          document.removeEventListener("scroll", lazyload);
          window.removeEventListener("resize", lazyload);
          window.removeEventListener("orientationchange", lazyload);
        }
      });
    };
    document.addEventListener("scroll", lazyload);
    window.addEventListener("resize", lazyload);
    window.addEventListener("orientationchange", lazyload);
profile
유연한 개발자

0개의 댓글