개인적으로 공부하면서 블로그를 작성하고 있으므로 내용이 정확하지 않을 수 있습니다.
잘못된 부분은 지적해주시면 개발자로서 성장하는데 많은 도움이 될 것 같습니다. 😀
Intersection Observer API(교차 관찰자 API)를 공부한 것을 바탕으로 웹 성능 최적화를 위한 이미지 지연 로딩을 구현해 보겠습니다.
이미지 지연 로딩은 웹 페이지 내부의 실제 이미지들이 실제로 화면에 보여질 필요가 있을 때 로딩을 할 수 있도록하는 기법이다. 웹 페이지 내에서 바로 로딩을 하지 않고 로딩 시점을 뒤로 미루는 것이라고 볼 수 있다.
페이지 초기 로딩 시 필요한 이미지의 수를 줄여 리소스 다운로드 용량을 줄일 수 있으며 불필요한 이미지 리소스 요청을 줄임으로써 다른 리소스들을 더 빠르게 처리할 수 있다. 즉, 사용자가 더 빠르게 페이지를 볼 수 있도록 도와준다.
이미지 리소스 전달은 주로 전송 바이트 수에 기반하여 비용이 청구된다. 이미지 지연 로딩은 이미지가 보여지지 않으면 절대 로딩하지 않으므로, 페이지 내에서 전달할 총 바이트를 줄일 수 있다. 따라서 비용을 감소 시킬 수 있다.
아래 두 가지 방법으로 이미지 지연 로딩을 구현해 볼 것이다.
loading
속성은 브라우저의 이미지 로드 시점 여부를 결정 할 수 있다. loading
속성의 값으로 lazy
를 사용하면 브라우저 스크롤에 반응해서 뷰포트 영역에 근접할 때 이미지를 로딩한다.
브라우저는 이미지가 뷰포트 영역에 교차할 때 로딩하지 않고 조금 더 미리 로딩한다. 주의할 사항은 아래 예제는 세로 스크롤을 하는 페이지이기 때문에 이미지에 높이 값이 없으면 지연 로딩이 동작하지 않는다.
// example code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
img {
width: 400px;
height: 1200px;
padding: 100px 0;
}
</style>
</head>
<body>
<img src="./images/img_1.jpg" loading="lazy"/>
<img src="./images/img_2.jpg" loading="lazy"/>
<img src="./images/img_3.jpg" loading="lazy"/>
<img src="./images/img_4.jpg" loading="lazy"/>
<img src="./images/img_5.jpg" loading="lazy"/>
<img src="./images/img_6.jpg" loading="lazy"/>
</body>
</html>
Intersection Observer API(교차 관찰자 API) 로 이미지 지연 로딩 구현은 아래와 같은 순서로 동작한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>이미지 지연 로딩(Intersection Observer)</title>
<style>
img {
display: block;
width: 800px;
height: 800px;
}
</style>
<script>
// 페이지의 리소스 로드가 완료되었을 때 실행
window.onload = function() {
const imgs = document.querySelectorAll('img');
// IntersectionObserver options
const options = {
// default browser viewport
root: null,
threshold: 0.5
};
// IntersectionObserver callback
const callback = function(entries, Observer) {
entries.forEach(entry => {
if(entry.isIntersecting) {
Observer.unobserve(entry.target);
entry.target.src = entry.target.dataset.src;
}
});
}
// IntersectionObserver 인스턴스 생성
let observer = new IntersectionObserver(callback, options);
// 타겟 요소를 순회하면서 관찰을 시작하도록 설정
for(let i = 0; i < imgs.length; i++) {
observer.observe(imgs[i]);
}
}
</script>
</head>
<body>
<img data-src="./images/img_1.jpg" alt="배경화면"/>
<img data-src="./images/img_2.jpg" alt="배경화면"/>
<img data-src="./images/img_3.jpg" alt="배경화면"/>
<img data-src="./images/img_4.jpg" alt="배경화면"/>
<img data-src="./images/img_5.jpg" alt="배경화면"/>
<img data-src="./images/img_6.jpg" alt="배경화면"/>
</body>
</html>
img tag loading="lazy" 속성을 활용한 이미지 지연 로딩은 간단하게 사용할 수 있지만 세세하게 컨트롤을 할 수 없다고 느껴졌으며 Intersection Observer API(교차 관찰자 API) 는 타겟 요소가 뷰포트와 교차했을 때 다양하고 세세한 컨트롤이 가능하다는 점이 정말 좋았다.