웹 성능 최적화 — 이미지, 폰트, 레이아웃 시프트 잡기
Lighthouse 100점을 위한 실전 웹 성능 최적화. 이미지, 폰트, CLS 해결법.
왜 성능이 중요한가
Google은 Core Web Vitals를 검색 랭킹 요소로 사용한다. 느린 사이트는 검색 순위가 낮아지고, 사용자 이탈률도 높아진다.
Core Web Vitals 세 가지:
- LCP (Largest Contentful Paint): 주요 콘텐츠 로딩 — 2.5초 이내
- INP (Interaction to Next Paint): 인터랙션 반응 — 200ms 이내
- CLS (Cumulative Layout Shift): 레이아웃 흔들림 — 0.1 이하
Karnel Labs 사이트는 Lighthouse Performance 100점이다. 어떻게 달성했는지 정리한다.
이미지 최적화
이미지는 웹 페이지에서 가장 큰 리소스다. 최적화하지 않으면 LCP를 망친다.
1. 포맷 선택
PNG → 투명 배경이 필요한 경우에만
JPEG → 사진에 적합하지만, WebP가 더 낫다
WebP → 대부분의 경우 최적 (30% 더 작은 파일)
AVIF → WebP보다 작지만 브라우저 지원이 제한적
2026년 기준 WebP가 가장 안전한 선택이다. 모든 모던 브라우저가 지원한다.
2. 크기 지정
<!-- 나쁜 예: 크기 없음 → CLS 유발 -->
<img src="/hero.webp" alt="Hero" />
<!-- 좋은 예: 크기 명시 → 브라우저가 공간을 미리 확보 -->
<img src="/hero.webp" alt="Hero" width="1200" height="630" />
width와 height를 명시하면 브라우저가 이미지 로딩 전에 공간을 확보한다. CLS가 0이 된다.
3. 레이지 로딩
뷰포트 밖의 이미지는 나중에 로딩한다.
<!-- 히어로 이미지: 즉시 로딩 -->
<img src="/hero.webp" alt="Hero" loading="eager" fetchpriority="high" />
<!-- 하단 이미지: 레이지 로딩 -->
<img src="/feature.webp" alt="Feature" loading="lazy" />
fetchpriority="high"는 LCP 이미지에 사용한다. 브라우저에게 이 이미지를 최우선으로 다운로드하라고 알려준다.
4. 반응형 이미지
<img
src="/hero-800.webp"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1200px"
alt="Hero"
/>
모바일에서 1200px 이미지를 다운로드할 필요가 없다. srcset으로 화면 크기에 맞는 이미지를 제공한다.
폰트 최적화
웹 폰트는 렌더링을 차단할 수 있다. 폰트가 로딩될 때까지 텍스트가 안 보이거나(FOIT), 기본 폰트로 보이다가 바뀌는(FOUT) 현상이 발생한다.
1. preconnect
폰트 서버와 미리 연결을 맺어둔다.
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
DNS 조회 + TCP 연결 + TLS 핸드셰이크 시간을 절약한다.
2. font-display
@font-face {
font-family: 'Geist';
src: url('/fonts/geist.woff2') format('woff2');
font-display: swap;
}
font-display: swap은 폰트 로딩 중 시스템 폰트를 먼저 보여준다. 텍스트가 바로 보이므로 LCP가 좋아진다.
3. 서브셋팅
한글 폰트는 글리프가 수천 개라 파일이 크다. 사용하는 글자만 추출하면 크기를 90%까지 줄일 수 있다.
# pyftsubset으로 서브셋 생성
pip install fonttools brotli
pyftsubset font.ttf --output-file=font-subset.woff2 --flavor=woff2 \
--text-file=used-characters.txt
CDN에서 제공하는 폰트(Google Fonts, jsDelivr)는 자동으로 서브셋을 적용한다.
CLS (레이아웃 시프트) 잡기
CLS는 사용자가 보고 있는 동안 레이아웃이 밀리는 현상이다. 버튼을 누르려는데 광고가 로딩되면서 다른 걸 눌러본 경험이 있을 것이다.
주요 원인과 해결
| 원인 | 해결 |
|---|---|
| 이미지 크기 미지정 | width/height 명시 또는 aspect-ratio |
| 웹 폰트 FOUT | font-display: optional 또는 size-adjust |
| 동적 콘텐츠 삽입 | min-height로 공간 미리 확보 |
| 광고 영역 | 고정 크기 컨테이너 사용 |
aspect-ratio
이미지 크기를 모를 때 비율로 공간을 확보한다.
.hero-image {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
}
측정 도구
- Lighthouse: Chrome DevTools → Lighthouse 탭
- PageSpeed Insights: 실제 사용자 데이터(CrUX) 기반
- Web Vitals Extension: Chrome 확장으로 실시간 CWV 모니터링
Karnel Labs 사이트 성능
| 항목 | 점수 |
|---|---|
| Performance | 100 |
| LCP | 0.5초 |
| INP | 0ms (JS가 거의 없으므로) |
| CLS | 0 |
비결:
- Astro로 JS 최소화
- 이미지에 width/height + loading=“lazy”
- 폰트 preconnect + CDN 서브셋
- Cloudflare 엣지 캐시
정리
웹 성능 최적화는 세 가지에 집중하면 된다. 이미지를 WebP로 적절한 크기에 제공하고, 폰트를 preconnect + swap으로 처리하고, 모든 동적 요소에 공간을 미리 확보한다. Lighthouse 100은 특별한 기술이 아니라, 기본기의 결과다.