- Published on
이미지 Lazy Loading
- Authors

- Name
- dwook
useEffect(() => {
const images = [
'/static/images/encarpay/encarpay_loan_menual_1.jpg',
'/static/images/encarpay/encarpay_loan_menual_2.jpg',
'/static/images/encarpay/encarpay_loan_menual_3.jpg'];
let count = 0;
const onload = () => {
count += 1;
if (count === images.length) {
setLoaded(true);
}
};
for (let i = 0; i < images.length; i += 1) {
const img = new Image();
img.src = images[i];
img.onload = onload;
}
}, []);
- Image 객체
<img>태그에 의해 생성된 DOM 객체. 자바스크립트로 이미지를 다룰 수 있음- 프로퍼티
- src: 이미지 경로
- width: 이미지가 출력될 폭. 이미지가 로드되면 실제폭으로 변경.
- height: 이미지가 출력될 높이. 이미지가 로드되면 실제폭으로 변경.
- complete: 이미지의 로드이 완료되었는지 여부. true/false
- onload: 이미지 로드 완료 후 호출되는 함수
Lazy Loading 기술
<img> 태그를 이용하는 일반적인 방법
- [1단계] 이미지 로딩을 사전에 막기
<img>태그에 src 속성이 있으면, 브라우저는 이미지를 무조건 로드.- 이미지 로딩을 지연시키려면 src 속성 대신 다른 속성에다가 이미지 url을 넣음.
<img data-src="https://ik.imagekit.io/demo/default-image.jpg" />
[2단계] 자바스크립트 이벤트를 이용하여 이미지로드
유저 사용성 때문에 처음 3개의 이미지는 미리 로딩되어 있음.
<img src="https://ik.imagekit.io/demo/img/image1.jpeg?tr=w-400,h-300" />
<img src="https://ik.imagekit.io/demo/img/image2.jpeg?tr=w-400,h-300" />
<img src="https://ik.imagekit.io/demo/img/image3.jpg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image4.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image5.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image6.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image7.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image8.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image9.jpeg?tr=w-400,h-300" />
<img class="lazy" data-src="https://ik.imagekit.io/demo/img/image10.jpeg?tr=w-400,h-300" />
document.addEventListener("DOMContentLoaded", function() {
var lazyloadImages = document.querySelectorAll("img.lazy");
var lazyloadThrottleTimeout;
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);
});
[2단계] Intersection Observer API를 이용하여 이미지 로드
이미지 로드를 지연시키기 위해 모든 이미지에 옵저버를 부착
엘리먼트가 뷰포트에 들어간 것을 API가 감지했을 때, isIntersecting 속성을 이용해서 URL을 data-src 속성에서 src 속성으로 이동시켜서 브라우저가 이미지를 로드
전부 로드되면 lazy 클래스명을 이미지에서 삭제하고 부착했던 옵저버를 제거
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);
}
})
- 임계점을 500px 주는 경우
$(document).ready(function() {
var lazyloadImages;
if ("IntersectionObserver" in window) {
lazyloadImages = document.querySelectorAll(".lazy");
var imageObserver = new IntersectionObserver(function(entries, observer) {
console.log(observer);
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var image = entry.target;
image.src = image.dataset.src;
image.classList.remove("lazy");
imageObserver.unobserve(image);
}
});
}, {
root: document.querySelector("#container"),
rootMargin: "0px 0px 500px 0px"
});
lazyloadImages.forEach(function(image) {
imageObserver.observe(image);
});
} else {
var lazyloadThrottleTimeout;
lazyloadImages = $(".lazy");
function lazyload () {
if(lazyloadThrottleTimeout) {
clearTimeout(lazyloadThrottleTimeout);
}
lazyloadThrottleTimeout = setTimeout(function() {
var scrollTop = $(window).scrollTop();
lazyloadImages.each(function() {
var el = $(this);
if(el.offset().top < window.innerHeight + scrollTop + 500) {
var url = el.attr("data-src");
el.attr("src", url);
el.removeClass("lazy");
lazyloadImages = $(".lazy");
}
});
if(lazyloadImages.length == 0) {
$(document).off("scroll");
$(window).off("resize");
}
}, 20);
}
$(document).on("scroll", lazyload);
$(window).on("resize", lazyload);
}
})
Native Lazy Loading 방식
- Chrome 76에서는 Native Lazy Loading을 지원
- loading 속성에 사용할 수 있는 값
- lazy: 뷰포트에서 일정한 거리에 닿을 때까지 로딩을 지연
- eager: 페이지가 로딩되자마자 해당 요소를 로딩
- auto: loading 속성을 쓰지 않을 것과 동일
- Native Lazy Loading을 지원하지 않는 브라우저의 경우, 위 방식을 이용
- 로딩 지연된 이미지들이 다운로드될 때 감싸고 있는 내용들이 밀려나는 것을 방지하려면, 반드시 height와 width 속성을
<img>태그에 추가하거나 inline style로 직접값을 지정
<img src="example.jpg" loading="lazy" alt="…" width="200" height="200">
<img src="example.jpg" loading="lazy" alt="…" style="height:200px; width:200px;">
<iframe src="example.html" loading="lazy"></iframe>
CSS 속성 중 Background Image를 Lazy Loading 하는 방법
- 이 예시에서 주목할 점은 lazy loading 관련해서 구현한 자바스크립트 코드가 똑같다는 것.
- ID bg-image를 가진 엘리먼트는 지정된 background-image를 가집니다. lazy라는 클래스를 엘리먼트에 추가하게 되면, 해당 엘리먼트는 background-image 속성을 none으로 변경.
- 브라우저는 초기에 엘리먼트에게 background-image: none 속성을 적용. 이후에 엘리먼트의 lazy 클래스를 삭제함으로써, background image를 로드하여 적용하게 됨.
<div id="bg-image" class="lazy"></div>
#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;
}
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.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);
}
})
참조링크