
앞서 Sticky navigation 구현 포스트에서 소개했던 Intersection Observer API를 활용하여 이번에는 lazy loading이라는 UX를 구현해 보자.
웹페이지 내에 이미지와 같이 용량이 큰 요소들이 포함되는 경우 구형 기기나 속도가 느린 네트워크 환경에서 이미지가 늦게 표시되는 현상이 발생할 수 있다. 이를 방지하기 위해 사용자가 특정 위치(ex. 로드할 이미지보다 100px 위)로 스크롤하는 시점에 이미지가 표시되도록 하는 것을 lazy loading이라 한다.
구현할 예제에서는 처음 웹페이지가 로드될 때에는 아래와 같이 크기와 용량이 작은 이미지에 블러 처리하여 표시하다가, 아래로 스크롤하며 이미지가 뷰포트의 200px만큼 올라온 시점에서 원본 이미지로 대체하고 블러 처리를 제거하여 표시한다.

이미지에 해당하는 HTML을 살펴보면 다음과 같다. img 태그의 src가 저용량의 이미지 경로로 되어 있다. lazy loading될 때 src는 아래 data-src의 원본 이미지 경로로 대체될 것이다.
<section class="section" id="section--1">
<div class="section__title">
<h2 class="section__description">Features</h2>
<h3 class="section__header">
Everything you need in a modern bank and more.
</h3>
</div>
<div class="features">
<img
src="img/digital-lazy.jpg"
data-src="img/digital.jpg"
alt="Computer"
class="features__img lazy-img"
/>
...
그리고 아래 css는 블러 처리 효과를 주는 lazy-img 클래스이다. 원본 이미지가 로드될 때 img태그의 class에서 이 클래스를 제거한다.
.lazy-img {
filter: blur(20px);
}
이제 lazy loading을 구현해 보자.
// Lazy loading images
const imgTargets = document.querySelectorAll('img[data-src]');
const loadImg = function (entries, observer) {
const [entry] = entries;
if (!entry.isIntersecting) return;
// Replace src with data-src
entry.target.src = entry.target.dataset.src;
entry.target.addEventListener('load', function () {
entry.target.classList.remove('lazy-img');
});
observer.unobserve(entry.target);
};
const imgObserver = new IntersectionObserver(loadImg, {
root: null,
threshold: 0,
rootMargin: '200px',
});
imgTargets.forEach(img => imgObserver.observe(img));
결과는 다음과 같다. 명확한 결과 확인을 위해 개발자 도구의 Network 탭에서 네트워크 환경을 요즘 네트워크 환경보다 조금 느린 Fast 3G로 설정하여 시뮬레이션하였다.
