img·iframe의 loading 속성으로 리소스를 언제 내려받을지 정한다. eager(기본)는 태그를 만나자마자, lazy는 화면에 가까워질 때까지 미룬다. JS(JavaScript) 없이 동작하는 브라우저 기본 기능이라 IntersectionObserver로 직접 만들 필요가 없다.
lazy의 "가까움" 기준은 브라우저가 정한다(Chrome은 네트워크 속도에 따라 수백~수천 px 여유를 둔다)- 화면 밖 이미지는 아예 받지 않으므로 처음 내려받는 바이트와 요청 수가 크게 준다
진짜 효과: 중요한 것을 먼저 받게 하기
이미지는 CSS나 동기 스크립트와 달리 렌더링을 막지 않는다. 다 받지 못해도 페이지는 그려지고 자리만 비어 있다. 그런데도 eager 이미지가 많으면 간접적으로 느려진다.
- 대역폭 경쟁: 수십 장이 동시에 받아지면 CSS·폰트·JS·LCP(Largest Contentful Paint) 이미지와 대역폭을 나눠 쓴다. HTTP/2여도 파이프는 하나다
- 디코드: 받은 이미지는 압축을 풀어야 그려진다. 큰 이미지 여럿을 동시에 풀면 스크롤이 버벅일 수 있다(
decoding="async"로 완화) - 메모리: 풀린 비트맵이 RAM(Random Access Memory)을 차지해 저사양 기기에 부담이다
브라우저도 화면 밖 eager 이미지의 우선순위를 낮추긴 하지만, 결국 전부 받고 큐를 차지한다. 그리드 상품 이미지에 lazy를 걸면 이미지 바이트뿐 아니라 LCP도 좋아지는 이유가 이 경쟁이 풀려서다.
주의
- 첫 화면의 주인공 이미지(LCP 후보)에는 lazy를 쓰지 않는다. 다운로드가 늦게 시작돼 LCP가 나빠진다. 오히려 우선순위를 올린다 → 이미지 로딩 우선순위(loading·fetchpriority), preload
width·height(또는 aspect-ratio)를 꼭 준다. 늦게 들어오는 이미지가 레이아웃을 밀면 CLS(Cumulative Layout Shift)가 나빠진다display: none으로 숨긴 lazy 이미지는 Chrome·Safari·Firefox 모두 받지 않는다. 반면opacity: 0처럼 다른 방식으로 숨기면 받는다- Next.js
Image는 기본이 lazy이고, 우선 로드 옵션을 주면 eager + preload로 바뀐다. 옵션 이름은 버전에 따라 다르다 → Next.js 16 변경점
출처: MDN — Lazy loading: Loading attribute · web.dev — Browser-level image lazy loading for the web