β보μ΄λ μκ°λ§ λ‘λνλ€β
μΉνμ΄μ§κ° λλ¦° μ΄μ λ λ¨μν μ½λκ° λ§μμκ° μλλλ€.
μ΄κΈ° λ‘λ© μ νμνμ§ μμ 리μμ€κΉμ§ λͺ¨λ λΆλ¬μ€κΈ° λλ¬Έμ
λλ€.
μ΄λ―Έμ§, λμμ, μΈλΆ μ€ν¬λ¦½νΈ, μ°¨νΈ λΌμ΄λΈλ¬λ¦¬β¦
μ¬μ©μκ° μμ§ μ€ν¬λ‘€νμ§λ μμλλ° λͺ¨λ λ€μ΄λ‘λλλ€λ©΄,
λ€νΈμν¬λ βνμ μλ μΌβμ λ¨Όμ μ²λ¦¬νκ³ μλ μ
μ΄μ£ .
μ΄ λ¬Έμ λ₯Ό ν΄κ²°νλ κ²μ΄ λ°λ‘ Lazy Loading (μ§μ° λ‘λ©) μ
λλ€.
μ¦, βμ¬μ©μκ° μ€μ λ‘ λ³Ό λλ§ λ¦¬μμ€λ₯Ό λΆλ¬μ€λ κΈ°μ βμ
λλ€.
Lazy Loadingμ λΈλΌμ°μ μ κΈ°λ³Έ μλ λ°©μμ βμ§μ°μμΌβ νμν μκ°μλ§ λ€νΈμν¬ μμ²μ λ°μμν€λ μ λ΅μ λλ€.
| κ΅¬λΆ | κΈ°μ‘΄ λ°©μ | Lazy Loading λ°©μ |
|---|---|---|
| λ‘λ© μμ | νμ΄μ§ μ΄κΈ° λ‘λ© μ | λ·°ν¬νΈ(νλ©΄)μ λ€μ΄μ¬ λ |
| λ€μ΄λ‘λ | μ λΆ λμμ | νμν 리μμ€λ§ μμ°¨μ μΌλ‘ |
| μ¬μ©μ κ²½ν | μ΄κΈ°μ λλ¦Ό | μ΄κΈ°μ κ°λ³κ³ λΉ λ¦ |
| λ€νΈμν¬ λΆν | λμ | λΆμ°λ¨ |

Before vs After Lazy Loading
λΈλΌμ°μ κ° νΉμ μμκ° λ·°ν¬νΈμ μ§μ
νλμ§ κ°μνλ κΈ°λ₯μ
λλ€.
μ΄λ κ°μ λμμ΄ νλ©΄μ λ±μ₯νλ©΄ μ΄λ―Έμ§ λ‘λ μμ²μ νΈλ¦¬κ±°ν©λλ€.
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src; // μ§μ
μ μ΄λ―Έμ§ λ‘λ
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll("img[data-src]").forEach((img) => observer.observe(img));
return () => observer.disconnect();
}, []);
isIntersecting: ν΄λΉ μμκ° λ·°ν¬νΈ μμΌλ‘ λ€μ΄μλκ°observer.unobserve: ν λ² λ‘λλ μμλ λ€μ κ°μνμ§ μμdata-src: μμ§ λ‘λνμ§ μμ μ΄λ―Έμ§ URLμ μμλ‘ λ³΄κ΄
Intersection Observer μλ μ리
rootλ λΈλΌμ°μ μ Viewportμ΄λ©°, κ°μ κΈ°μ€μ΄ λλ μμμ
λλ€.trueλ νμ¬ Viewport μμ μλ μμ, falseλ νλ©΄ λ°μ μλ μμλ₯Ό μλ―Έν©λλ€.root κ²½κ³μ μ λλ¬νλ©΄ Intersection Observerκ° μ΄λ₯Ό κ°μ§νμ¬ Lazy Loading, Infinite Scroll, μ λλ©μ΄μ
μ€ν λ±μ λ‘μ§μ μλμΌλ‘ μνν©λλ€.loading="lazy"μ΅κ·Ό λλΆλΆμ λΈλΌμ°μ (Chrome, Edge, Firefox)λ HTML μμ± νλλ‘ κΈ°λ³Έ Lazy Loadingμ μ§μν©λλ€.
<img src="photo.jpg" loading="lazy" alt="sample image" />
μ΄ μμ± νλλ‘ μ΄λ―Έμ§ λ‘λ© νμ΄λ°μ μλμΌλ‘ μ΅μ νν μ μμ΅λλ€.
Chromeμ κΈ°λ³Έμ μΌλ‘ λ·°ν¬νΈμ μ½ 1250px κ·Όμ²μ μ§μ
νλ©΄ μμ²μ μμν©λλ€.
π§ Tip:
loading="lazy"λ<iframe>νκ·Έμλ μ μ©λ©λλ€.
β YouTube, Map, κ΄κ³ λ±μ embed μ½ν μΈ λ₯Ό λ¦κ² λ‘λν μ μμ΅λλ€.
rootMarginκ³Ό μ¬μ λ‘λ© κ±°λ¦¬const observer = new IntersectionObserver(callback, {
rootMargin: "200px",
});
rootMarginμ νλ©΄ κ²½κ³μ μλ€ μ¬μ 거리μ
λλ€.200pxλ‘ μ€μ νλ©΄, μ΄λ―Έμ§κ° νλ©΄μ 200px μ μ 미리 λ‘λλ©λλ€.π§ μΆμ² κ°:
200px ~ 400px(μ¬μ©μ μ€ν¬λ‘€ μλμ λ°λΌ μ‘°μ )

Intersection Observer μμ± κ°λ
λ
rootMarginκ³Ό threshold μ€μ μ μ€ν¬λ‘€ μ λ λΆλλ¬μ΄ μ¬μ©μ κ²½νμ μ 곡ν©λλ€.import { useEffect, useRef, useState } from "react";
function LazyImage({ src, alt }) {
const [isVisible, setVisible] = useState(false);
const imgRef = useRef(null);
useEffect(() => {
const io = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) setVisible(true);
}, { rootMargin: "300px" });
if (imgRef.current) io.observe(imgRef.current);
return () => io.disconnect();
}, []);
return (
<img
ref={imgRef}
src={isVisible ? src : "/placeholder.jpg"}
alt={alt}
loading="lazy"
style={{ width: "100%", borderRadius: "8px" }}
/>
);
}
export default function Gallery() {
return (
<section>
<h2>Lazy Loaded Gallery</h2>
<div className="grid">
{Array.from({ length: 12 }, (_, i) => (
<LazyImage
key={i}
src={`/images/photo-${i + 1}.jpg`}
alt={`photo-${i}`}
/>
))}
</div>
</section>
);
}

Lazy Loading λμ μμ
| λ¬Έμ | μ€λͺ | ν΄κ²°μ± |
|---|---|---|
| β οΈ LCP μ§μ° | νμ΄λ‘ μ΄λ―Έμ§(LCP ν보)λ₯Ό Lazy μ²λ¦¬νλ©΄ νλ©΄ νμκ° λ¦μ΄μ§ | LCP μ΄λ―Έμ§λ loading="eager" λλ fetchpriority="high" |
| β οΈ SEO λ¬Έμ | ꡬκΈλ΄μ΄ JSλ₯Ό μ€ννμ§ μμΌλ©΄ Lazy μ΄λ―Έμ§ μΈμ λΆκ° | SSR λλ <noscript> ν΄λ°± μ΄λ―Έμ§ μΆκ° |
| β οΈ κΉλΉ‘μ | rootMargin λ무 μμ κ²½μ° | 200~400pxλ‘ μν |
| β οΈ λ μ΄μμ νλ€λ¦Ό | μ΄λ―Έμ§ λμ΄κ° μ§μ λμ§ μμΌλ©΄ CLS λ°μ | width, height, λλ aspect-ratio μ§μ |
| β οΈ μ μ¬μ κΈ°κΈ° | μ€ν¬λ‘€ μ΄λ²€νΈ/κ΄μ°°μ μκ° λ§μΌλ©΄ CPU μ μ | IntersectionObserver μ¬μ¬μ©μΌλ‘ κ°μ μ μ΅μν |
| λꡬ | νμΈν νλͺ© |
|---|---|
| Chrome DevTools β Network | μ΄λ―Έμ§ μμ²μ΄ μ€ν¬λ‘€ μμ μ λ°μνλμ§ |
| Performance ν | μ€ν¬λ‘€ μ€ FPS μ μ§, Paint μ΅μν |
| Lighthouse | βDefer offscreen imagesβ κ°μ νμΈ |
| WebPageTest / GTMetrix | Time to Interactive, Total Bytes κ°μ |

Lazy Loading μ±λ₯ λΉκ΅ κ·Έλν
Lazy Loadingμ βκΈ°μ μ μΌλ‘ μ΄λ ΅μ§ μμλ°, κ°μ₯ ν¨κ³Όκ° ν° μ΅μ νβμ
λλ€.
μ΄λ―Έμ§ ν μ₯λ§ μ€μ¬λ LCPκ° μ λ° κ°κΉμ΄ κ°μ λλ κ²½μ°λ μμ΅λλ€.
κ°μ₯ μ€μν μμΉμ λ¨ νλμ λλ€.
βμ¬μ©μκ° μ€μ λ‘ λ³΄λ μκ°μλ§ λ‘λνλΌ.β
νμν μμ μ νμν 리μμ€λ₯Ό 보λ΄λ©΄, μ¬μ©μ κ²½νμ μμ°μ€λ½κ² λΆλλ½κ³ λΉ λ₯Έ μλΉμ€λ‘ λ°λλλ€.
Intersection Observer APIμ μλ μ리μ μμ±λ€ λν΄ κ·Έλ¦Όκ³Ό ν¨κ» μ€λͺ ν΄μ£Όμ μ Intersection Observer APIλ₯Ό μ²μ μ ν¨μλ μ΄ν΄κ° λ무 μ λμ΄μ!!
LCP λΉκ΅λ₯Ό ν΅ν΄ Lazy Loadingμ΄ μΌλ§λ ν¨κ³Όμ μΈμ§λ μ μ μμ΄ λμμ΄ λμ΅λλ€