페이지 속도 최적화: Lighthouse 지표 개선 완전 가이드
Google Lighthouse의 6개 지표(Performance·Accessibility·Best Practices·SEO·FCP·TTI) 분석법과 리소스 압축·캐싱·CDN 활용 최적화 전략을 다룹니다.
페이지 속도와 SEO의 관계
Google은 2021년부터 Core Web Vitals를 공식 랭킹 팩터로 적용했습니다. 그러나 속도가 SEO에 미치는 영향은 순위 뿐만이 아닙니다.
| 속도의 영향 | 데이터 |
|---|---|
| 페이지 로드 3초 초과 시 이탈률 | 32% 증가 (Google, 2017) |
| 1초 지연 시 전환율 하락 | 7% 감소 (Akamai) |
| Amazon 100ms 지연 시 매출 손실 | 1% 감소 (Amazon 내부 연구) |
| LCP 2.5초 미만 사이트의 Google 순위 우선성 | 콘텐츠 품질 동일 시 우선 순위 |
Lighthouse 점수 이해하기
Lighthouse는 Chrome DevTools에 내장된 웹 성능 측정 도구입니다. 성능 점수는 다음 지표들의 가중 평균으로 계산됩니다.
| 지표 | 약자 | 가중치 | 설명 |
|---|---|---|---|
| First Contentful Paint | FCP | 10% | 첫 번째 콘텐츠 요소가 화면에 나타나는 시간 |
| Largest Contentful Paint | LCP | 25% | 가장 큰 콘텐츠 요소 렌더링 시간 |
| Total Blocking Time | TBT | 30% | 메인 스레드가 50ms 이상 차단된 총 시간 합계 |
| Cumulative Layout Shift | CLS | 25% | 예기치 않은 레이아웃 이동 누적 점수 |
| Speed Index | SI | 10% | 페이지 콘텐츠가 시각적으로 채워지는 속도 |
TBT(30%)가 가장 높은 가중치를 가집니다. TBT는 실험실 데이터(lab data)에서 INP와 높은 상관관계를 보이며, JavaScript 실행 시간 최적화가 점수 향상에 가장 큰 효과를 냅니다.
렌더 블로킹 리소스 제거
렌더 블로킹 리소스는 HTML 파싱 중 브라우저가 페이지 렌더링을 중단하고 해당 파일을 먼저 로드하게 만드는 CSS·JavaScript 파일입니다. Lighthouse에서 "Eliminate render-blocking resources" 경고로 표시됩니다.
<!-- ❌ 렌더 블로킹 -->
<link rel="stylesheet" href="/style.css">
<script src="/app.js"></script>
<!-- ✅ 개선된 방법 -->
<!-- 중요 CSS는 <style> 인라인으로 (Critical CSS) -->
<style>/* 첫 화면에 필요한 최소 CSS */</style>
<!-- 나머지 CSS는 비동기 로드 -->
<link rel="preload" as="style" href="/style.css"
onload="this.onload=null;this.rel='stylesheet'">
<!-- JS는 defer 또는 async -->
<script src="/app.js" defer></script>
<script src="/analytics.js" async></script>
| 방법 | 동작 | 사용처 |
|---|---|---|
async | 다운로드 비동기, 실행은 즉시 (HTML 파싱 중단) | 독립적 외부 스크립트 (Analytics, 광고) |
defer | 다운로드 비동기, 실행은 HTML 파싱 완료 후 | DOM에 의존하는 앱 스크립트 |
| Critical CSS | 첫 화면 필요 CSS만 인라인으로 <head>에 | 히어로 영역, 네비게이션 등 |
JavaScript 번들 최적화
현대 웹 앱에서 JavaScript는 TBT를 높이는 가장 큰 원인입니다. 번들 크기를 줄이고 코드 분할(Code Splitting)을 통해 초기 로드를 최소화해야 합니다.
핵심 전략
- Tree Shaking: 사용하지 않는 코드 제거. Webpack·Rollup·esbuild 등에서 자동 지원
- Code Splitting: 라우트 단위로 번들 분리. Next.js는 자동으로 페이지별 코드 분할
- Dynamic Import: 필요한 시점에만 모듈 로드
- Third-party 최소화: 불필요한 npm 패키지 제거 (bundlephobia.com에서 패키지 크기 확인)
// 동적 import로 무거운 라이브러리 지연 로드
const { default: Chart } = await import('chart.js')
// Next.js dynamic import
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
ssr: false, // 서버 렌더링 제외
})
CDN·캐싱·압축 최적화
CDN (Content Delivery Network)
CDN은 전 세계 여러 서버에 콘텐츠를 분산해 사용자와 가장 가까운 서버에서 파일을 제공합니다. 이미지·JavaScript·CSS 등 정적 파일에 CDN을 적용하면 TTFB(Time to First Byte)를 크게 줄일 수 있습니다.
HTTP 캐싱 헤더 설정
# 정적 파일 (JS, CSS, 이미지) — 1년 캐시 Cache-Control: public, max-age=31536000, immutable # HTML 페이지 — 캐시 없음 (항상 최신 내용) Cache-Control: no-cache, no-store, must-revalidate
압축 (Gzip / Brotli)
| 압축 방식 | 압축률 | CPU 비용 | 권장 |
|---|---|---|---|
| Brotli (br) | Gzip보다 20~26% 우수 | 약간 높음 | 최우선 권장 |
| Gzip | 기준 | 낮음 | Brotli 미지원 대비 fallback |
자주 묻는 질문 (FAQ)
Q. Lighthouse 점수가 100점이면 SEO에 유리한가요?
Q. async와 defer의 차이는 무엇인가요?
async는 스크립트를 비동기로 다운로드하고 다운로드 완료 즉시 실행(HTML 파싱 중단)합니다. defer는 비동기로 다운로드하되 HTML 파싱 완료 후에 실행합니다. DOM에 의존하는 앱 스크립트는 defer, 독립적인 외부 스크립트(Analytics, 광고)는 async가 적합합니다.