정의
이미지가 여러 장인 가로 캐러셀에서, 지금 화면에 없는 슬라이드 이미지까지 한꺼번에 받아버려 첫 화면이 느려질 때 쓰는 기법이다. 캐러셀 뷰포트 이미지 지연은 가로 슬라이더에서 현재 뷰포트(지금 화면에 실제로 보이는 영역)에 들어오지 않은 슬라이드에 대해 <img>(또는 이미지 컴포넌트)를 아예 DOM에 붙이지 않고(mount하지 않고) 고정 높이 placeholder(자리만 잡아두는 빈 박스)만 둔다. 첫 번째로 보이는 슬라이드(보통 LCP=가장 큰 콘텐츠 요소 후보)는 eager(지연 없이 즉시 로드)·high priority(높은 다운로드 우선순위)를 유지한다.
왜 필요한가
캐러셀 첫 카드 썸네일은 흔히 그 페이지의 가장 큰 콘텐츠(LCP) 요소다. 그런데 슬라이드마다 이미지를 전부 붙이면, 브라우저와 preload scanner(렌더 전에 문서를 훑어 받아둘 리소스를 미리 찾는 장치)가 대용량 이미지 여러 개를 동시에 요청해 총 전송량과 메인 스레드의 이미지 디코딩 부담이 커진다 — 정작 중요한 첫 이미지 도착이 늦어진다. loading="lazy"(뷰포트 근처에 올 때까지 로드를 미루는 속성)만으로는 DOM에 <img>가 이미 있으면 브라우저가 그걸 발견(discover)해 우선순위 경쟁이 남을 수 있어, 화면 밖 슬라이드는 조건부로 아예 mount하지 않는 편이 더 확실하다.
동작 원리
- 캐러셀 라이브러리(Embla, Swiper 등) 또는
IntersectionObserver로 in-view 인덱스를 구한다. shouldLoad = inViewSet.has(index)일 때만 Image 컴포넌트 렌더.- 외부 wrapper는 고정 height — 이미지 없을 때도 레이아웃 유지(pop-in CLS 방지).
- index 0(첫 visible):
fetchPriority="high",loading="eager".
| 슬라이드 | Image mount | Priority |
|---|---|---|
| First visible | Yes | high |
| Scrolled into view | Yes | lazy/auto |
| Out of view | No (placeholder) | — |
실무 적용
Embla 예: emblaApi.slidesInView() + slidesInView 이벤트로 state 갱신.
검증: Network transfer before/after, LCP element가 여전히 첫 썸네일인지 trace로 확인.
트레이드오프
| 선택 | 이득 | 비용 |
|---|---|---|
| slidesInView defer | Transfer·decode ↓ | 스크롤 시 placeholder→image pop-in |
| 전부 lazy | 구현 간단 | mount·discover 비용 잔존 |
| 전부 eager | 스크롤 미리보기 빠름 | LCP·대역폭 악화 |
pop-in을 줄이려면 placeholder와 이미지가 동일 aspect box(absolute inset-0)를 공유해야 한다.
사용하면 안 되는 경우
- 슬라이드 1~2장뿐(이득 미미).
- 모든 슬라이드가 narrow viewport에서 동시에 보임.
- 썸네일이 이미 매우 작고 총 transfer가 무시 가능한 수준.
흔한 실수
- lazy만 적용하고 off-viewport에도 Image 노드를 mount.
- 모든 카드에
priority— LCP 후보 하나만 high. - placeholder 높이 없이 defer → scroll 시 CLS.
관련 개념
- largest-contentful-paint — LCP 후보 식별
- resource-priority — fetchpriority·preload 범위
- cls-skeleton-layout-reservation — placeholder 박스 모델