모바일에서 화면 밖 이미지 지연: 네이티브 지연 로딩 가이드
네이티브 지연 로딩, content-visibility, 그리고 JavaScript 이미지 지연이 무의미한 이유

모바일 이미지 지연: 표준
모바일 페이지는 제한된 연결과 제한된 CPU를 두고 경쟁합니다. 초기에 로드하는 화면 밖의 모든 이미지는 초기 페인트에 실제로 중요한 이미지와 스크립트에서 대역폭을 빼앗습니다.
최종 검토: Arjen Karel, 2026년 3월

Table of Contents!
1. 모바일에서 화면 밖 이미지 지연: 네이티브 지연 로딩
브라우저가 페이지를 로드할 때 제한된 수의 병렬 연결을 엽니다(여러 요인에 따라 다르지만 도메인당 6개가 일반적인 평균입니다). 이러한 연결이 화면 밖의 이미지(예: 푸터 로고 또는 캐러셀 슬라이드)를 다운로드하는 데 사용되는 경우 중요한 리소스(일반적으로 LCP 이미지, 중요한 스크립트 및 폰트) 다운로드가 슬롯과 대역폭을 놓고 경쟁하게 됩니다. 이는 네트워크 경합이며 Core Web Vitals를 직접적으로 저하시킵니다.
네이티브 loading 속성을 사용하여 화면 밖 이미지를 지연시키면 중요한 것에 우선순위를 지정할 수 있습니다. 브라우저는 즉시 보이는 것만 가져와서 Largest Contentful Paint (LCP) 및 First Contentful Paint (FCP)에 영향을 주는 에셋을 위해 대역폭을 확보합니다. 네이티브 지연 로딩은 이 우선순위 지정을 브라우저의 자체 메커니즘으로 오프로드하며, 이는 더 빠르고 JavaScript 지연 로딩 라이브러리의 필요성을 제거합니다.
구현
초기 뷰포트("폴드(fold)") 아래의 모든 이미지에 대해 loading="lazy" 속성을 추가하세요.
<!-- 표준 지연 이미지 -->
<img src="product-detail.jpg"
alt="섀시 측면 뷰"
width="800"
height="600"
decoding="async">
width 및 height 속성은 필수적입니다. 이 속성이 없으면 브라우저가 이미지가 로드되기 전에 공간을 예약할 수 없어 layout shift (CLS)가 발생합니다. 모바일 페이지의 62%는 여전히 최소 하나 이상의 이미지에 명시적인 크기를 설정하지 못하고 있습니다.
모바일에서 지연 로딩이 작동하는 방식: 브라우저 휴리스틱
네이티브 지연 로딩은 브라우저가 유효 연결 유형(ECT)에 따라 로딩 임계값(이미지 다운로드가 트리거되는 시점)을 조정하기 때문에 JavaScript 솔루션보다 우수합니다.
- 4G/WiFi 환경: Blink 엔진(Chrome/Edge)은 약 1,250px의 보수적인 임계값을 사용합니다. 짧은 지연 시간을 가정하며 사용자가 비교적 가까이 스크롤할 때만 이미지를 가져옵니다.
- 3G/Slow-2G 환경: 임계값이 약 2,500px로 확장됩니다. 브라우저는 높은 왕복 시간을 보상하기 위해 훨씬 일찍 요청을 시작하므로 사용자가 뷰포트로 스크롤하기 전에 이미지가 준비됩니다.
2025 Web Almanac에 따르면, 중앙값에 해당하는 모바일 페이지는 총 911KB에 달하는 15개의 이미지를 로드합니다. 이 이미지 중 약 26%만이 loading="lazy"를 사용합니다. 나머지는 제한된 연결을 놓고 경쟁하며 열성적(eagerly)으로 로드됩니다. 일반적인 4G 모바일 연결에서 이는 LCP 이미지가 사용자가 몇 초 동안 보지 못할 수십 개의 이미지 뒤에서 대기 상태에 갇혀 있음을 의미합니다.
중대한 예외: LCP 후보
흔한 성능 저하 원인: Largest Contentful Paint 요소(일반적으로 히어로 이미지)에 loading="lazy"를 적용하는 것입니다. 이는 레이아웃이 완료될 때까지 가져오기를 지연시킵니다.
Google의 연구에 따르면 LCP 이미지를 지연 로딩하면 중앙값 LCP가 624ms 추가됩니다. 이는 이론적인 위험이 아닙니다. 2025 Web Almanac에 따르면 모바일 페이지의 17%가 여전히 이러한 실수를 범하고 있습니다. Lighthouse가 이를 지적하는 경우 지연 로드된 LCP 경고 해결 방법을 참조하세요.
LCP 이미지는 반드시 eager-load 되어야 하며 우선순위가 지정되어야 합니다:
<!-- 히어로 이미지: 즉시 및 우선순위 지정 -->
<img src="hero.jpg"
alt="여름 컬렉션"
width="1200"
height="800"
>
loading="lazy"를 fetchpriority="high"와 결합하지 마세요. 이 둘은 서로 모순됩니다: lazy는 브라우저에게 기다리라고 지시하고, high는 서두르라고 지시합니다. 브라우저는 lazy가 설정되어 있으면 우선순위 힌트를 무시합니다. 브라우저가 리소스 우선순위를 지정하는 방법에 대한 자세한 내용은 리소스 우선순위 지정 가이드를 참조하세요.
2. 모바일의 복잡성: 뷰포트와 터치
모바일 뷰포트는 정적이지 않습니다. 사용자가 스크롤하거나 기기를 회전하거나 URL 표시줄을 축소하도록 트리거함에 따라 가시 영역이 변경됩니다. 이 부분이 네이티브 지연 로딩이 JavaScript 솔루션보다 실질적인 이점을 가지는 곳입니다.
- 뷰포트(Viewport): 브라우저 창의 가시적인 직사각형 영역입니다. 모바일에서는 동적이며 기기 방향(세로 모드와 가로 모드)과 브라우저 크롬의 상태(URL 표시줄 축소)에 따라 크기가 변경됩니다.
- 폴드(The Fold): 뷰포트의 정확한 하단 모서리입니다. 가시적인 콘텐츠와 화면 밖 콘텐츠를 구분하는 임계값입니다.
- 폴드 위(Above the Fold): 스크롤하지 않고 페이지 로드 시 즉시 볼 수 있는 모든 콘텐츠입니다. 이곳의 이미지는 매우 중요하며 절대 지연 로드되어서는 안 됩니다.
- 폴드 아래(Below the Fold): 폴드를 지나 수직으로 위치한 모든 콘텐츠입니다. 이 콘텐츠는 중요하지 않으며 사용자가 근처로 스크롤할 때까지 지연되어야 합니다.

동적 뷰포트
모바일 브라우저에서 뷰포트 높이(vh)는 유동적입니다. 사용자가 터치 스크롤을 시작하면 URL 표시줄과 탐색 컨트롤이 자주 축소되어 가시 영역 크기가 변경됩니다.
JavaScript 지연 로딩 라이브러리는 일반적으로 페이지 로드 시 한 번만 뷰포트 높이(window.innerHeight)를 계산합니다. 모바일 브라우저가 스크롤 중에 URL 표시줄을 숨겨 가시 영역의 크기를 동적으로 조정할 때 JavaScript 메서드는 이전의 더 작은 높이 값을 계속 사용합니다. 이미지가 확장된 뷰포트에 들어가더라도 로드되지 않은 상태로 유지되어 방문자에게 빈 자리 표시자(placeholder)를 유발합니다.
브라우저의 내부 레이아웃 엔진은 시각적 뷰포트를 자동으로 추적하므로 뷰포트 크기 변경에 관계없이 네이티브 지연 로딩 트리거가 실행됩니다. 이것이 JavaScript 대안보다 네이티브 지연 로딩을 선호해야 하는 한 가지 이유입니다.
3. 모바일 이미지 디코딩 및 CPU 스로틀링
모바일 기기는 CPU가 제한되어 있으며 모바일에서의 이미지 디코딩은 상대적으로 느리고 비용이 많이 들 수 있습니다. JPEG를 비트맵으로 변환하려면 많은 CPU 사이클이 필요합니다. 모바일 프로세서에서 더 큰 이미지 시퀀스를 디코딩하면 기본 스레드가 각각 50ms에서 100ms 동안 차단되어 입력 지연이 발생할 수 있습니다.
해결책: content-visibility
CSS 속성 content-visibility: auto는 지연 렌더링 역할을 합니다. 이는 브라우저에 화면 밖 요소에 대한 레이아웃 및 페인팅 단계를 완전히 건너뛰도록 지시합니다. 요소는 DOM에 존재하지만 뷰포트에 접근할 때까지 Render Tree에는 존재하지 않습니다.
이 최적화는 요소의 하위 트리(subtree) 렌더링을 건너뛰는 방식으로 작동하므로 <img> 태그(하위 트리가 없음)에 직접 적용할 수 없습니다. 이미지와 해당 콘텐츠를 호스팅하는 제품 컨테이너 또는 이미지 카드에 content-visibility를 적용하세요:
@media (max-width: 768px) {
.image-card, .product-card {
/* 컨테이너와 그 자식들의 렌더링을 건너뜀 */
content-visibility: auto;
/* 필수: 컨테이너가 0px 높이로 축소되는 것을 방지함 */
contain-intrinsic-size: auto 300px;
}
}
이렇게 하면 이미지가 다운로드되더라도 사용자가 실제로 스크롤할 때까지 브라우저가 레이아웃/페인트 비용을 지불하지 않습니다.
content-visibility는 Safari 18이 지원을 시작한 2024년 9월에 Baseline 상태에 도달했습니다. 이제 전 세계 브라우저의 93%에서 작동합니다. Google의 벤치마크에 따르면 화면 밖 섹션이 많은 페이지의 초기 로드 시 렌더링 성능이 7배 향상되는 것으로 나타났습니다.
실제 기기에서 렌더링 개선을 확인하고 싶다면, Real User Monitoring이 모바일 트래픽 전반에 걸친 실제 INP 및 LCP 영향을 보여줄 것입니다. CoreDash가 모니터링하는 사이트 전체에서, 제품 그리드에 content-visibility: auto를 사용하는 페이지는 사용하지 않는 페이지에 비해 모바일에서 약 15% 더 나은 INP를 보여줍니다.
4. 레거시 방법론: 이를 피해야 하는 이유
loading="lazy"가 브라우저 지원을 받기 전에는 JavaScript가 유일한 옵션이었습니다. 전 세계 95% 지원을 받는 네이티브 지연 로딩을 갖춘 지금, 이러한 JavaScript 방식은 기술 부채입니다. 이를 제거하세요.
스크롤 핸들러의 시대 (2010년~2016년)
초기 구현은 스크롤 이벤트에 이벤트 리스너를 연결했습니다.
// 구식: 사용하지 마세요
window.addEventListener('scroll', () => {
images.forEach(img => {
if (img.getBoundingClientRect().top < window.innerHeight) {
img.src = img.dataset.src;
}
});
});
메인 스레드 차단(Main Thread Blocking): 스크롤 이벤트는 초당 수십 번 발생합니다. 활발한 스크롤 중에 로직을 실행하고 레이아웃을 계산(getBoundingClientRect)하면 프레임 드롭(jank)이 발생합니다.
레이아웃 스래싱(Layout Thrashing): 기하학적 속성을 쿼리하면 브라우저가 모바일 CPU에서 계산 비용이 많이 드는 작업인 레이아웃을 동기적으로 다시 계산하도록 강제합니다.
IntersectionObserver의 시대 (2016년~2019년)
IntersectionObserver API는 요소 가시성의 변화를 비동기식으로 관찰하여 성능을 개선했습니다.
// 레거시: 가능한 한 네이티브 loading="lazy"를 선호하세요
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
스크립트 종속성(Script Dependency): JavaScript 실행이 필요합니다. 기본 스레드가 프레임워크(React/Vue) 하이드레이션으로 바쁜 경우 이미지가 뷰포트에 있더라도 로드되지 않은 상태로 유지됩니다.
네트워크 인식 부족(Lack of Network Awareness): 네이티브 로딩과 달리 IntersectionObserver는 고정된 여백(예: rootMargin: '200px')을 사용합니다. 느린 네트워크에서 버퍼를 자동으로 확장하지 않으므로 연결 상태가 좋지 않은 사용자의 경우 빈 화면이 깜박일 수 있습니다.
지연 로딩을 넘어서는 이미지 최적화 기법에 대한 전체적인 개요나 (loading="lazy"가 다루지 않는) CSS 배경 이미지 지연에 대해 알아보려면 해당 전용 가이드를 참조하세요.

