The Complete Guide to Lazy Loading Images

May 14, 2020 - 4 minute read -
Web

이 글은 The Complete Guide to Lazy Loading Images을 번역/요약 한 글입니다.

Images Lazy Loading

  • 이미지를 불러올 때 한 번에 다 부르지 않고, 필요 할 때 적절하게 가져옴.
    • 웹 페이지 로드가 훨씬 빠르다
    • 다운로드 수도 적으니 당연히 비용 절약

Lazy Loading Techniques for Images

        <img src="/path/to/some/image.jpg" />
    

위와 같이 src attribute를 이용하면 이미지가 몇 개든간에 브라우저는 다 로드해버린다.

        <img data-src="https://ik.imagekit.io/demo/default-image.jpg" />
    

따라서, src 대신에 data-src를 이용해서 일단 브라우저가 이미지를 로드하는 것을 막는다.

그 다음, image가 viewport내에 있는지 확인 후, src를 이용해 이미지를 로드하도록 한다.

  • resize (창 크기), scroll(스크롤), orientationChange(가로/세로 변환) 이벤트를 감지해서 image의 위치가 viewport내에 있는지 계산 후 로드 하는 방법
  • IntersectionObserver API를 통해서 이미지가 뷰포트에 들어가는 것을 감지하는 방법

전자의 경우 각각의 이벤트를 바인딩하거나, throtlling, 위치 계산 등의 이유로 퍼포먼스/복잡도 측면에서 후자에 비해 확실히 구리지만 후자의 경우 API를 지원하지 않는 브라우저가 있음.

      document.addEventListener("DOMContentLoaded", function() {
        var lazyloadImages;    
        if ("IntersectionObserver" in window) {
          lazyloadImages = document.querySelectorAll(".lazy");
          var imageObserver = new IntersectionObserver(function(entries, observer) {
            entries.forEach(function(entry) {
              if (entry.isIntersecting) {
                var image = entry.target;
                image.src = image.dataset.src;
                image.classList.remove("lazy");
                imageObserver.unobserve(image);
              }
            });
          });
          lazyloadImages.forEach(function(image) {
            imageObserver.observe(image);
          });
        } else {  
          var lazyloadThrottleTimeout;
          lazyloadImages = document.querySelectorAll(".lazy");
          function lazyload () {
            if(lazyloadThrottleTimeout) {
              clearTimeout(lazyloadThrottleTimeout);
            }    
            lazyloadThrottleTimeout = setTimeout(function() {
              var scrollTop = window.pageYOffset;
              lazyloadImages.forEach(function(img) {
                  if(img.offsetTop < (window.innerHeight + scrollTop)) {
                    img.src = img.dataset.src;
                    img.classList.remove('lazy');
                  }
              });
              if(lazyloadImages.length == 0) { 
                document.removeEventListener("scroll", lazyload);
                window.removeEventListener("resize", lazyload);
                window.removeEventListener("orientationChange", lazyload);
              }
            }, 20);
          }
          document.addEventListener("scroll", lazyload);
          window.addEventListener("resize", lazyload);
          window.addEventListener("orientationChange", lazyload);
        }
      })
  

Lazy Loading CSS Background Images

background-image를 lazy loading하는 것도 크게 다르지 않다.

    #bg-image.lazy {
       background-image: none;
       background-color: #F1F1FA;
    }
    #bg-image {
      background-image: url("https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-600,h-400");
      max-width: 600px;
      height: 400px;
    }

위와 같이 일단 CSS 규칙을 적어 놓고 로드가 필요할 때 자바스트립트로 lazy class를 때면 됨.
(id 혼자 쓰이는 거보다 class & id로 쓰이는게 우선순위가 높다.)

Creating a Better User Experience

  • Placeholder
    • 이미지 로드 전에 해당 이미지와 비슷한 계열의 배경색을 놓는다.
  • Low Quality Image Placeholder (LQIP)
    • 이미지 로드 전에 해당 이미지를 블러 처리한 느낌의 저퀄리티 이미지를 놓는다.
  • Add Buffer Time for Images to Load
    • viewport와 image 위치를 계산할 때 margin을 줘서 미리미리 로드하도록 하자.
  • Avoid Content Reflow
    • 이미지를 감싸는 컨테이너 사이즈를 고정하지 않으면 이미지가 로드되는 순간, 다른 엘리먼트들을 밀어내서 Reflow가 일어남.
      참고) goes into great dept
  • Avoid Lazy Loading Every Image
    • 첫 번째 페이지 요소들은 Lazy Loading 할 필요 없다. 유저 경험을 더 구리게 할 수 있음.
    • 디바이스 사이즈에 따라 로드되어야 할 이미지 수도 다르다는 것을 인지하자.

Testing Lazy Load

개발자 도구에서 Network > Images로 이미지가 lazy하게 잘 로드 되는지 확인 가능.



Resource

The Complete Guide to Lazy Loading Images