웹을 위한 네이티브 이미지 레이지 로딩 - codepink/myspace GitHub Wiki

웹을 위한 네이티브 이미지 레이지 로딩

원문 : https://addyosmani.com/blog/lazy-loading/

이번 글에서는, 웹에 네이티브 <img><iframe>을 가져오는 새로운 loading 속성을 살펴보려고 한다! 궁금한 부분은 다음과 같다.

<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>

우리는 Chrome 75 버전부터 loading 속성을 지원할 예정이고 곧 출시될 이 기능을 열심히 연구하고 있다. 그때까지 loading 속성이 어떻게 동작하는지 살펴보자.

소개

웹 페이지는 종종 많은 이미지들을 포함하고 있어 데이터 사용, 페이지 팽창(page-bloat) 및 페이지를 로드 속도에 영향을 미친다. 이러한 이미지들 대부분은 오프스크린 이미지(offscreen, 화면 밖의 이미지)로, 이 이미지들을 보기 위해서는 스크롤을 해야한다.

전통적으로 오프스크린 이미지가 페이지 로드 시간에 미치는 영향을 제한하기 위해, 개발자들은 사용자가 스크롤해서 이미지를 가져오기 전까지 이미지들을 지연시켜서 가져올 수 있도록 LazySizes와 같은 자바스크립트 라이브러리를 사용했었다.

211개의 이미지를 로딩하는 페이지다. 레이지 로딩이 없는 버전의 경우, 10MB의 이미지 데이터를 가져온다. LazSizes와 같은 라이브러리를 사용해 레이지 로딩을 할 경우, 250KB에 해당하는 이미지만 먼저 가져온다. 다른 이미지들은 사용자 스크롤을 통해서 가져온다. WPT를 참조한다.

만약 브라우저에서 이러한 오프스크린 이미지들을 로드하지 않으면 어떻게 될까? 뷰포트의 콘텐츠를 빠르게 로드할 수 있고, 전체 네트워크 데이터 사용량과 저가형 장치에서 메모리 사용량을 줄일 수 있다. 필자는 조만간 이미지와 아이프레임을 위한 새로운 loading 속성을 제공할 수 있을 것 같아 기쁘다.

loading 속성

loading 속성은 사용자가 스크롤 할 때까지 브라우저가 오프스크린 이미지와 아이프레임 로드를 지연할 수 있도록 한다. loading 속성은 세 가지 값을 가진다.

  • lazy : 레이지 로딩를 한다.
  • eager : 레이지 로딩을 하지 않는다. 즉시 로드한다.
  • auto : 브라우저가 레이지 로드 여부를 판단한다.

속성을 지정하지 않으면 loading=auto로 설정한 것과 같다.

imgiframe을 위한 loading 속성은 HTML 표준의 한 부분으로 동작한다.

예제

loading 속성은 <img>(srcset 속성을 가지거나 <picture> 태그에 포함된 경우)와 <iframe>에서 잘 동작한다.

<!-- 사용자 스크롤이 발생하면 근처의 오프스크린 이미지를 레이지 로드한다. -->
<img src="unicorn.jpg" loading="lazy" alt=".."/>

<!-- 레이지 로드 대신 이미지를 바로 로드한다. -->
<img src="unicorn.jpg" loading="eager" alt=".."/>

<!-- 브라우저가 이미지를 레이지 로드할지 여부를 결정한다. -->
<img src="unicorn.jpg" loading="auto" alt=".."/>

<!-- <picture> 태그 안에 이미지를 레이지 로드한다.
<img>는 <picture> 태그와 srcset 속성에 할당된 이미지의 폴백이다. -->
<picture>
  <source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
  <source srcset="small.jpg 1x, small-hd.jpg 2x">
  <img src="fallback.jpg" loading="lazy">
</picture>

<!-- srcset 속성이 정의된 이미지를 레이지 로드한다. -->
<img src="small.jpg"
     srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w"
     sizes="(min-width: 36em) 33.3vw, 100vw"
     alt="A rad wolf" loading="lazy">

<!-- 사용자 스크롤이 발생하면 근처의 오프스크린 아이프레임을 레이지 로드한다. -->
<iframe src="video-player.html" loading="lazy"></iframe>

"사용자 스크롤 근처일 때"를 찾는 정확한 발견법(heuristics)은 브라우저에 남아 있다. 일반적으로 브라우저는 뷰포트에 오기 전에 지연시킨 이미지와 아이프레임 콘텐츠를 가져오기 시작할 것이다. 이렇게하면 사용자가 스크롤 할 때 이미지 또는 아이프레임 로드가 완료된 변경 사항이 증가한다.

참고: 필자는 decoding 속성과 가깝도록 loading으로 속성명을 제안했다. 여러 값(lazy, eager, auto)을 지원해야하므로 lazyload와 같은 속성명은 제안하지 않았다.

기능 탐지

우리는 레이지 로딩용 자바스크립트 라이브러리(크로스 브라우저 지원을 위해)를 가져와서 적용할 수 있어야 한다는 점을 고려했었다. 다음과 같이 loading 속성은 기능 탐지로 지원할 수 있다.

<script>
if ('loading' in HTMLImageElement.prototype) { 
   // 브라우저가 `loading` 속성을 지원한다.
} else {
   // 레이지 로딩을 대신하기위해 폴리필/자바스크립트 라이브러리를 가져와서 적용한다.
}
</script>

참고: loading 속성은 점진적인 개선(progressive enhancement)에도 사용할 수 있다. 이 속성을 지원하는 브라우저들은 loading=lazy로 새로운 레이지 로딩 동작을 얻을 수 있고 그렇지 않은 브라우저들은 이미지를 그대로 로드한다.

이미지 레이지 로딩을 위한 크로스 브라우저

레이지 로딩 이미지에 대한 브라우저간 지원이 중요하다면, 마크업에 <img src=unicorn.jpg loading=lazy />를 사용할 때 라이브러를 기능 탐지하고 레이지 로드하는 것만으로는 충분하지 않다.

새로운 속성을 지원하지 않는 브라우저에서 항상 로드(eager load)하는 것을 피할 수 있도록 <img data-src=unicorn.jpg />와 같이 어떤 속성(src, srcset 또는 <source> 대신)을 사용하는 마크업이 필요하다. loading 속성을 지원하는 경우 자바스크립트를 사용해 해당 속성을 적절한 것으로 변경하고 그렇지 않으면 라이브러리를 로드한다. 이것이 하이브리드 레이지 로딩이다.

아래는 이것이 어떻게 생겼는지 보여주는 예다.

  • 뷰포트 안에서 스크롤 없이 볼 수 있는 이미지는 일반적인 <img> 태그이다. data-src 속성은 프리로드 스캐너(preload scanner)를 무시하므로, 뷰포트에서 로드 가능성이 있는 모든 것을 피하려고 할 때 사용할 수 있다.
  • loading 속성을 지원하지 않는 브라우저에서 이미지가 항상 로드되지 않도록 이미지에 data-src 속성을 사용한다. loading 속성을 지원하면, data-src를 src로 변경한다.
  • loading 속성을 지원하지 않을 때 LazySizes와 같은 라이브러리를 폴백으로 로드하고 초기화한다. 아래 예제에서는, 느리게 로드되기 원하는 LazySizes 이미지를 나타내기 위해 class=lazyload를 사용했다.
<!-- 이 뷰포트 안의 이미지는 정상적으로 로드한다. -->
<img src="hero.jpg" alt=".."/>

<!-- 나머지 이미지는 레이지 로드한다. -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
  if ('loading' in HTMLImageElement.prototype) {
      const images = document.querySelectorAll("img.lazyload");
      images.forEach(img => {
          img.src = img.dataset.src;
      });
  } else {
      // LazySizes 라이브러리를 동적으로 가져오기
      let script = document.createElement("script");
    script.async = true;
    script.src =
      "https://cdnjs.cloudflare.com/ajax/libs/lazysizes/4.1.8/lazysizes.min.js";
    document.body.appendChild(script);
  }
</script>

다음은 위에서 폴백 라이브러리를 가져오는 것을 수행하기 위해 동적 가져오기(dynamic import)를 사용한 방법이다.

<!--  뷰포트 안의 이미지는 정상적으로 로드한다. -->
<img src="hero.jpg" alt=".."/>

<!-- 나머지 이미지는 레이지 로드한다. -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
(async () => {
    if ('loading' in HTMLImageElement.prototype) {
        const images = document.querySelectorAll("img.lazyload");
        images.forEach(img => {
            img.src = img.dataset.src;
        });
    } else {
        // LazySizes 라이브러리를 동적으로 가져오기
        const lazySizesLib = await import('/lazysizes.min.js');
        // LazySizes 초기화 (data-src & class=lazyload를 읽는다)
        lazySizes.init(); // lazySizes 인스턴스는 글로벌로 동작한다.
    }
})();
</script>

Andrea Verlicchi는 하이브리드 레이지 로딩라는 좋은 글을 썼다.

데모

정확히 100마리 새끼 고양이 사진으로 구현한 loading=lazy 데모를 사용할 수 있다. 확인해보라!

youtube 또한 위에서 확인할 수 있는 기능 동작을 녹화했다.

크롬 구현 상세

_loading 속성은 프로덕션 버전에서 사용하기 전에

⚠️ **GitHub.com Fallback** ⚠️