웹 애플리케이션 성능 저하의 주범, 그리고 Lazy Loading의 필요성
현대의 웹 애플리케이션에서 이미지는 사용자 경험(UX)과 웹 성능을 좌우하는 핵심 요소 중 하나입니다. 고해상도 이미지 사용 증가와 더불어, 페이지 내 이미지 콘텐츠의 비중이 커지면서 웹사이트 로딩 속도 저하는 피할 수 없는 문제가 되었습니다. 모든 이미지를 초기 로딩 시점에 한꺼번에 불러오는 방식은 다음과 같은 치명적인 성능 병목 현상을 야기합니다.
-
초기 로딩 시간 증가 (FCP, LCP 지연): 뷰포트 밖에 있는 이미지까지 다운로드함으로써, 사용자가 실제로 콘텐츠를 볼 수 있게 되는 시점(First Contentful Paint)과 가장 큰 콘텐츠 요소가 로드되는 시점(Largest Contentful Paint)이 크게 지연됩니다. 이는 Core Web Vitals 지표에도 악영향을 미칩니다.
-
불필요한 대역폭 소모: 사용자가 스크롤하여 보지 않을 이미지에 대해서도 네트워크 리소스를 낭비하게 되어 모바일 환경에서는 데이터 요금 부담을 가중시키고, 데스크톱 환경에서도 불필요한 네트워크 요청으로 다른 중요한 리소스 로딩을 방해합니다.
-
서버 부하 증가: 모든 이미지에 대한 요청이 동시에 발생하여 서버에 과도한 부하를 주며, 이는 서비스 안정성 저하로 이어질 수 있습니다.
이러한 문제들을 해결하기 위한 가장 효과적인 전략 중 하나가 바로 이미지 Lazy Loading입니다. Lazy Loading은 웹 페이지의 초기 로딩 시점에 모든 이미지를 불러오는 대신, 사용자의 뷰포트(viewport) 내에 들어올 때 또는 임계치에 도달했을 때 해당 이미지를 비동기적으로 로드하는 기술입니다.
이미지 Lazy Loading의 작동 원리 및 적용 방법
Lazy Loading을 적용하는 주요 방식은 크게 두 가지로 나눌 수 있으며, 각각의 장단점을 이해하고 프로젝트의 요구사항에 맞춰 선택해야 합니다.
1. 브라우저 Native Lazy Loading (loading="lazy"
)
가장 간단하면서도 강력한 방법으로, HTML <img>
태그에 loading="lazy"
속성을 추가하는 것만으로 구현할 수 있습니다. 최신 브라우저 대부분에서 지원하며, 브라우저 자체적으로 최적화된 방식으로 Lazy Loading을 처리합니다.
<img src="placeholder.jpg"
data-src="actual-image.jpg"
alt="Description of image"
loading="lazy"
width="500"
height="300">
-
장점:
-
구현 용이성: 별도의 JavaScript 코드 없이 HTML 속성 추가만으로 적용 가능합니다.
-
성능 최적화: 브라우저 엔진 레벨에서 구현되므로, JavaScript 기반 솔루션보다 더 효율적이고 빠르게 동작할 수 있습니다.
-
개발자 부담 감소: 복잡한 Intersection Observer API나 이벤트 리스너 관리 없이 브라우저가 알아서 처리합니다.
-
-
단점:
-
브라우저 지원: 모든 레거시 브라우저에서 완벽하게 지원되지 않을 수 있습니다. (그러나 대부분의 현대 브라우저는 지원)
-
세밀한 제어 부족: 로딩 임계값(threshold)이나 로딩 전략을 개발자가 직접 세밀하게 제어하기 어렵습니다.
-
권장 사항: 특별한 제어 요구사항이 없다면, Native Lazy Loading을 우선적으로 적용하는 것이 좋습니다.
2. JavaScript 기반 Lazy Loading (Intersection Observer API 활용)
보다 세밀한 제어가 필요하거나, loading="lazy"
속성을 지원하지 않는 브라우저에 대한 폴백(fallback)이 필요할 때 JavaScript 기반 Lazy Loading을 구현할 수 있습니다. 과거에는 스크롤 이벤트 리스너를 활용하는 방식이 주로 사용되었으나, 성능 이슈로 인해 현재는 Intersection Observer API를 활용하는 것이 표준으로 자리 잡았습니다.
기본적인 구현 로직:
-
초기에는 이미지의
src
속성에 Placeholder 이미지를 지정하거나,data-src
속성에 실제 이미지 URL을 저장합니다. -
Intersection Observer 인스턴스를 생성하고, 이미지 요소들을 감시(observe)합니다.
-
이미지 요소가 뷰포트에 진입하거나, 정의된 임계값에 도달하면 Observer 콜백 함수가 실행됩니다.
-
콜백 함수 내에서
data-src
에 저장된 실제 이미지 URL을src
속성으로 이동시켜 이미지를 로드합니다. -
이미지 로딩이 완료되면 해당 이미지 요소에 대한 관찰을 중단(unobserve)합니다.
// HTML (예시)
// <img class="lazyload" data-src="actual-image.jpg" alt="Description">
// JavaScript
document.addEventListener("DOMContentLoaded", function() {
const lazyImages = [].slice.call(document.querySelectorAll("img.lazyload"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
// lazyImage.srcset = lazyImage.dataset.srcset; // srcset도 필요하다면
lazyImage.classList.remove("lazyload");
lazyImageObserver.unobserve(lazyImage);
}
});
}, {
// Root Margin: 뷰포트 가장자리로부터 이미지를 미리 로드할 영역 (px 또는 %)
// 예: "0px 0px 200px 0px" 는 하단으로 200px 미리 로드
rootMargin: "0px 0px 200px 0px"
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// IntersectionObserver를 지원하지 않는 브라우저를 위한 폴백
// (예: 스크롤 이벤트 리스너를 사용하거나, 모든 이미지를 즉시 로드)
// 이 부분은 성능상 권장되지 않으므로, 주요 대상 브라우저를 고려하여 판단
lazyImages.forEach(function(lazyImage) {
lazyImage.src = lazyImage.dataset.src;
});
}
});
-
인기 있는 JavaScript Lazy Loading 라이브러리:
-
lazysizes: 매우 가볍고, SEO 친화적이며,
srcset
과picture
요소를 포함한 다양한 이미지 형식을 지원합니다. Native Lazy Loading의 폴백으로도 활용될 수 있습니다. -
lozad.js: 작은 번들 크기와 높은 성능을 자랑합니다.
-
Lazy Loading 적용 시 고려사항 및 최적화 팁
Lazy Loading은 강력한 성능 최적화 기술이지만, 모든 이미지에 무분별하게 적용하는 것은 오히려 사용자 경험을 해칠 수 있습니다.
- 'Above the Fold' 이미지 관리: 초기 화면(뷰포트)에 즉시 보이는 이미지(Above the Fold)에는 Lazy Loading을 적용해서는 안 됩니다. 이 이미지들은 페이지의 Largest Contentful Paint(LCP)에 직접적인 영향을 미치므로,
loading="eager"
를 명시하거나 Lazy Loading에서 제외하여 즉시 로드되도록 해야 합니다.
<img src="logo.png" alt="Company Logo" width="100" height="50" loading="eager">
-
width
및height
속성 명시: 이미지의width
와height
속성을 HTML에 명시하여 레이아웃 쉬프트(Cumulative Layout Shift, CLS)를 방지해야 합니다. 이미지가 로드되기 전에 브라우저가 이미지의 공간을 미리 확보할 수 있도록 하여, 로딩 중 레이아웃이 흔들리는 현상을 막습니다. 이는 Core Web Vitals 점수 향상에 필수적입니다. -
Placeholder 이미지 사용: Lazy Loading되는 이미지가 로드되기 전까지 사용자에게 시각적인 공백을 줄여주기 위해 흐릿한 저해상도 이미지나 단색 배경의 Placeholder를 사용하는 것이 좋습니다. 이는 사용자에게 페이지가 로드되고 있음을 알리는 역할을 합니다.
-
srcset
및<picture>
태그 활용: 반응형 이미지(srcset
)와 아트 디렉션(picture
태그)을 Lazy Loading과 함께 사용하여 다양한 화면 크기와 해상도에 최적화된 이미지를 제공하고, 불필요한 이미지 다운로드를 더욱 줄일 수 있습니다.
<picture>
<source srcset="image-large.webp" type="image/webp" media="(min-width: 1200px)" loading="lazy">
<source srcset="image-medium.webp" type="image/webp" media="(min-width: 768px)" loading="lazy">
<img src="placeholder.jpg" data-src="image-small.jpg" alt="Description" loading="lazy">
</picture>
결론: 성능과 사용자 경험, 두 마리 토끼를 잡는 Lazy Loading
이미지 Lazy Loading은 단순히 이미지 로딩을 지연시키는 것을 넘어, 웹 페이지의 초기 로딩 성능을 획기적으로 개선하고, 대역폭 사용을 효율화하며, 서버 부하를 줄여주는 필수적인 웹 최적화 기법입니다. 특히 Core Web Vitals와 같은 웹 성능 지표의 중요성이 증대되면서, Lazy Loading의 전략적인 적용은 개발자가 반드시 고려해야 할 사항이 되었습니다.
Native Lazy Loading을 우선적으로 고려하되, 세밀한 제어가 필요하거나 특정 브라우저 환경을 지원해야 할 경우 Intersection Observer API 기반의 JavaScript 구현 또는 라이브러리 활용을 검토하십시오. 또한, 'Above the Fold' 이미지 관리, width
/height
명시, Placeholder 사용 등의 최적화 팁을 함께 적용하여 사용자에게 빠르고 쾌적한 웹 경험을 제공할 수 있을 것입니다.
댓글이 없습니다.