웹을 위한 네이티브 이미지 레이지 로딩 - codepink/myspace GitHub Wiki
이번 글에서는, 웹에 네이티브 <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
속성은 세 가지 값을 가진다.
- lazy : 레이지 로딩를 한다.
- eager : 레이지 로딩을 하지 않는다. 즉시 로드한다.
- auto : 브라우저가 레이지 로드 여부를 판단한다.
속성을 지정하지 않으면 loading=auto
로 설정한 것과 같다.
img
와 iframe
을 위한 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 데모를 사용할 수 있다. 확인해보라!
_loading 속성은 프로덕션 버전에서 사용하기 전에