오늘은 등 운동을 조지고 자기 전에 lazy loading을 구현하는 2가지 방법을 정리해보려고 합니다. 방법은 아래와 같습니다.
먼저 html파일 구조를 확인하세요.
index.html
<div id="app">
<img
src="https://images.unsplash.com/photo-1606822350112-b9e3caea2461?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
src="https://images.unsplash.com/photo-1606936405812-1bdddbd08fd5?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0fHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
src="https://images.unsplash.com/photo-1606945094026-f93bf0b75ee0?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw3fHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
data-src=""
/>
<img
src="https://images.unsplash.com/photo-1606940077503-8cd3365e5cdc?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1fHx8ZW58MHx8fA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606854385394-14d09ac9e14e?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMXx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606726712298-a9d14f8297a0?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxM3x8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606895213457-36798be25021?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606828367227-4239d8229dbd?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxNnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606922619211-1448e81dd5bf?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyMnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606847993410-27c7ba6beeb8?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyN3x8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606895125340-0b56a65d52ff?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606823605375-455be4b8e84e?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyOHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606838830438-5f380a664a4e?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzM3x8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606840955672-94a03be58496?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzNnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606838830981-e71375a3744b?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzOHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606846646130-e38ef8be6ae5?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0MHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606838975183-abef02eea03e?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0Mnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606827729365-a54bc3802682?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0NXx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606835265808-8bac088170d9?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0NHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606838001695-c041b2bfcf0b?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1MHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606879992317-a7e2ee28f976?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1Nnx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
<img
data-src="https://images.unsplash.com/photo-1606825526677-5023c8a074d2?ixid=MXwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1OHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60"
/>
</div>
window 객체에 스크롤 이벤트를 걸어서 image DOM의 y축 좌표 위치를 추적합니다. 만약, DOM의 y축 거리가 우리가 설정한 거리보다 작아진다면 이미지 src를 할당해주면 됩니다. 말보단 코드겠죠? 아래 코드를 확인해보세요.
index.js
const images = document.querySelectorAll("img");
// scroll event를 활용한 lazy loading
window.addEventListener("scroll", () => {
images.forEach((image) => {
const rect = image.getBoundingClientRect();
if (!image.src && rect.top < window.innerHeight * 1.3) {
image.src = image.dataset.src;
}
});
});
우리가 정한 기준점에 태그가 들어왔을 때 data-src
에 넣어 뒀던 이미지 주소를 src
속성에 할당합니다. img 태그는 src
속성에 값을 받으면 이미지를 로딩한답니다.
참고로 images가 유사배열이라서 forEach 메서드를 못 쓸 줄 알았는데 확인해보니 prototype(NodeList)에 forEach 메서드가 있더군요. 😲
두 번째 방법은 브라우저가 제공하는 Web API인 IntersectionObserver를 활용하는 것입니다. IntersectionObserver를 모르셨던 분들은 잘 정리된 블로그 포스팅이 있으니 링크 걸어두겠습니다. (갓...)
Intersection Observer - 요소의 가시성 관찰
위에 걸어둔 포스팅을 공부하시고 아래 코드를 보면 이해하기 매우 쉬울겁니다.
const images = document.querySelectorAll("img");
const ioOptions = { rootMargin: "300px" }; // 화면에 나타나기전 미리 로딩을 위해!
const io = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
const target = entry.target;
if (!target.src) { // 미리 로드된 이미지 태그들을 제외해줍니다.
target.src = target.dataset.src;
}
}
});
}, ioOptions);
images.forEach((image) => io.observe(image));
오늘은 image lazy loading에 대해 알아봤습니다. 어플리케이션에서 performance를 결정하는 요소 중에 이미지가 차지하는 비율이 약 70%입니다. (어디서 본 것 같은데...) 따라서 이미지 최적화는 유저 경험을 생각했을 때 필수라고 생각합니다. 구현이 쉬워서 금방 익숙해질 것 같네요. 그럼 오늘도 수고했어여ㅎㅎ