stack-analysis
CHAPTER 92 / 120
읽기 약 2분
FUNCTION
이미지 최적화: WebP/AVIF + lazy + srcset
핵심 개념
next/image·sharp·responsive images·lazy loading — 이미지 90% 용량 감소.
본문
포맷 비교
| 포맷 | 압축률 | 호환성 | 적합 |
|---|---|---|---|
| JPEG | 기준 (100%) | 100% | 사진 (폴백) |
| PNG | 무손실 | 100% | 투명·아이콘 |
| WebP | 30% 작음 | 97% | 일반 사진 |
| AVIF | 50% 작음 | 92% | 최신 브라우저 |
| SVG | 벡터 | 100% | 아이콘·로고 |
→ AVIF → WebP → JPEG 순으로 폴백next/image 자동 변환
import Image from 'next/image';
<Image
src="/photo.jpg"
alt="제품 사진"
width={800}
height={600}
sizes="(max-width: 768px) 100vw, 50vw"
quality={80} // 기본 75
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
loading="lazy" // 기본 (priority 외)
/>
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60 * 60 * 24 * 365, // 1년
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example.com' },
],
},
};srcset + sizes — 반응형
<!-- 브라우저가 가장 적합한 크기 자동 선택 -->
<img
src="/photo-800w.jpg"
srcset="
/photo-400w.jpg 400w,
/photo-800w.jpg 800w,
/photo-1600w.jpg 1600w,
/photo-3200w.jpg 3200w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw
"
alt=""
loading="lazy"
/>
<!-- picture로 포맷 분기 -->
<picture>
<source srcset="/photo.avif" type="image/avif" />
<source srcset="/photo.webp" type="image/webp" />
<img src="/photo.jpg" alt="" />
</picture>sharp로 변환 (서버)
import sharp from 'sharp';
// 업로드 → 다중 사이즈·포맷 자동 생성
async function processUpload(buffer: Buffer, key: string) {
const sizes = [400, 800, 1600];
const formats = ['avif', 'webp', 'jpeg'];
for (const size of sizes) {
for (const fmt of formats) {
const output = await sharp(buffer)
.resize(size, null, { fit: 'inside', withoutEnlargement: true })
.toFormat(fmt as any, {
quality: fmt === 'avif' ? 60 : fmt === 'webp' ? 75 : 80,
})
.toBuffer();
await s3.send(new PutObjectCommand({
Bucket: BUCKET,
Key: `${key}-${size}.${fmt}`,
Body: output,
ContentType: `image/${fmt}`,
CacheControl: 'public, max-age=31536000, immutable',
}));
}
}
}Lazy Loading — Native + Polyfill
<!-- Native (97% 지원) -->
<img src="/photo.jpg" loading="lazy" decoding="async" alt="" />
<!-- IntersectionObserver fallback -->
<script>
if (!('loading' in HTMLImageElement.prototype)) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
const img = e.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
observer.observe(img);
});
}
</script>blur placeholder — UX
// plaiceholder로 빌드 타임 생성
import { getPlaiceholder } from 'plaiceholder';
import fs from 'fs/promises';
async function generateBlur(path: string) {
const buffer = await fs.readFile(path);
const { base64 } = await getPlaiceholder(buffer);
return base64; // data:image/jpeg;base64,...
}
// 또는 Sharp로 작은 LQIP (Low Quality Image Placeholder)
async function makeLQIP(buffer: Buffer) {
const tiny = await sharp(buffer).resize(20).webp({ quality: 30 }).toBuffer();
return `data:image/webp;base64,${tiny.toString('base64')}`;
}CDN 활용 — Cloudflare Images
// 이미지 업로드 → Cloudflare Images (자동 변환·CDN)
const formData = new FormData();
formData.append('file', file);
const res = await fetch(
`https://api.cloudflare.com/client/v4/accounts/.../images/v1`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${CF_API_TOKEN}` },
body: formData,
}
);
// 사용 — variant로 사이즈 지정
<img src="https://imagedelivery.net/.../photo-id/public" />
<img src="https://imagedelivery.net/.../photo-id/thumbnail" />
<img src="https://imagedelivery.net/.../photo-id/w=800" />
// 비용: $5/100K 이미지·월 + $1/100GB 전송다음 챕터
CH.93 "번들 최적화: Tree Shaking + Code Splitting".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 코드의 이미지 최적화 부분을 분석해서 실전 분석 + 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
이미지 최적화 관련 베스트 프랙티스 5가지를 비교 분석해서 패턴 추출를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 프로젝트 전체에서 이미지 최적화 최적화 가능 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 시장의 이미지 최적화 트렌드를 솔직히 알려줘.
⭐ 이것만 기억하세요
이미지 최적화: WebP/AVIF + lazy + srcset는 이 3가지만 확실히 잡으세요
1.AVIF → WebP → JPEG 폴백 = 모든 브라우저 + 50% 용량 감소
2.srcset + sizes로 디바이스 적합 사이즈 자동 선택
3.blur placeholder + lazy loading = UX·LCP 동시 개선
공유하기
진행도 92 / 120