이미지 최적화만으로 PageSpeed 20점 올리기 — 36MB를 0.8MB로
웹사이트 성능 최적화에서 가장 먼저 해야 할 일은 뭘까요? 코드 분할? 캐싱? 아닙니다. 이미지 압축입니다. 가장 쉽고, 가장 효과가 크고, 리스크도 가장 낮습니다.
이 글은 Next.js 사이트에서 이미지 6장(36MB)을 0.8MB로 압축하고, 로딩 전략을 개선해서 PageSpeed Desktop 점수를 28점 올린 과정을 정리한 것입니다.
36MB
압축 전 총 용량
0.8MB
압축 후 총 용량
97.8%
평균 감소율
+28점
Desktop 상승
1문제 — 이미지 6장, 36MB
사이트에 사용된 이미지는 총 6장이었습니다. 디자인 단계에서 고해상도 원본을 그대로 WebP로 변환만 한 상태였는데, 해상도는 원본 그대로였고 압축 수준도 기본값이었습니다.
| 이미지 | 용도 | 용량 | 해상도 |
|---|---|---|---|
| Hero 메인 | 첫 화면 전체 | 5.6MB | 1856×2304 |
| 서비스 (AI) | 서비스 카드 | 5.3MB | 1920×1434 |
| 서비스 (네트워크) | 서비스 카드 | 6.6MB | 1920×1434 |
| 서비스 (프로그램) | 서비스 카드 | 5.9MB | 1920×1434 |
| 배경 텍스처 | 섹션 배경 | 6.0MB | 1920×1072 |
| 배경 사진 | CTA 배경 | 6.1MB | 1920×1072 |
| 합계 | 35.6MB | ||
Google PageSpeed Insights는 Mobile을 Moto G Power + 슬로우 4G (1.6Mbps) 환경으로 시뮬레이션합니다. 이 속도로 36MB를 다운로드하면 약 180초가 걸립니다. LCP가 110초로 측정된 건 당연한 결과였습니다.
눈으로는 문제가 없습니다
5MB WebP 이미지와 100KB WebP 이미지의 차이는 대부분의 모니터에서 구분이 안 됩니다. 특히 웹 해상도(1920px 이하)에서는 quality 80이나 quality 100이나 육안으로 동일합니다. 용량이 클수록 좋다는 생각은 착각입니다.
2sharp로 압축하기
sharp는 Node.js에서 가장 빠른 이미지 처리 라이브러리입니다. libvips 기반이라 Photoshop이나 온라인 도구보다 훨씬 빠르고, 스크립트로 자동화할 수 있습니다. 구체적인 빌드 스크립트는 sharp 자동화 글을 참고하세요.
// 설치
npm install sharp --save-dev// 단일 이미지 압축
const sharp = require('sharp');
// WebP — 일반 이미지
await sharp('input.webp')
.resize(1920) // 너비 기준 리사이즈
.webp({ quality: 80 }) // quality 80이면 충분
.toFile('output.webp');
// AVIF — Hero 이미지 (더 공격적 압축)
await sharp('hero.webp')
.resize(900) // 모바일 기준 너비
.avif({ quality: 40, effort: 6 })
.toFile('hero.avif');압축 기준
| 용도 | 포맷 | 너비 | Quality | 비고 |
|---|---|---|---|---|
| Hero (LCP 이미지) | AVIF | ~900px | 35~50 | 가장 공격적으로 |
| 서비스 카드 | WebP | ~600px | 40~50 | lazy loading 적용 |
| 배경 텍스처 | WebP | ~1200px | 30~40 | 디테일 덜 중요 |
| 배경 사진 | WebP | ~1200px | 40~50 | lazy loading 적용 |
압축 결과
| 이미지 | 변경 전 | 변경 후 | 감소율 |
|---|---|---|---|
| Hero 메인 | 5,695KB | 124KB | 97.8% |
| 서비스 (AI) | 5,406KB | 93KB | 98.3% |
| 서비스 (네트워크) | 6,789KB | 214KB | 96.8% |
| 서비스 (프로그램) | 6,082KB | 155KB | 97.5% |
| 배경 텍스처 | 6,170KB | 122KB | 98.0% |
| 배경 사진 | 6,282KB | 97KB | 98.5% |
| 합계 | 35.6MB | 0.8MB | 97.8% |
3WebP vs AVIF
두 포맷 모두 최신 브라우저에서 지원되지만, 용도에 따라 선택이 달라집니다.
| 항목 | WebP | AVIF |
|---|---|---|
| 압축률 | 좋음 | 매우 좋음 (40~60% 더 작음) |
| 인코딩 속도 | 빠름 | 느림 (약 6배) |
| 디코딩 속도 | 빠름 | 약간 느림 |
| 브라우저 지원 | 96%+ | 92%+ |
| 화질 (같은 용량) | 좋음 | 더 좋음 |
실전 선택 기준
Hero 이미지(LCP)에는 AVIF를 씁니다. 1장이라 인코딩 시간은 무시할 수 있고, 용량 차이가 LCP에 직접 영향을 줍니다. 나머지 이미지는 WebP이면 충분합니다. 빌드 시간도 고려 대상입니다. 이미지가 수십 장이면 AVIF 인코딩 시간이 부담될 수 있습니다.
4로딩 전략 — fetchPriority와 lazy loading
이미지를 압축하는 것만으로는 부족합니다. 어떤 이미지를 먼저 로드할지 브라우저에게 알려줘야 합니다.
Above the Fold (Hero)
<img
src="/images/hero.avif"
alt="설명"
width={900}
height={1118}
fetchPriority="high"
/>- fetchPriority="high"로 최우선 로드
- width/height로 레이아웃 공간 확보
- loading="lazy" 넣으면 안 됨
Below the Fold (카드, 배경)
<img
src="/images/card.webp"
alt="설명"
width={600}
height={448}
loading="lazy"
/>- loading="lazy"로 뷰포트 진입 시 로드
- Hero와 대역폭 경쟁 방지
- 초기 로딩 데이터 절약
width와 height는 왜 필요한가
img 태그에 width와 height를 명시하면 브라우저가 이미지 로드 전에 정확한 공간을 확보합니다. 이미지가 로드되면서 아래 콘텐츠가 밀리는 현상(CLS)을 방지합니다. CSS로 크기를 제어하더라도 HTML 속성은 넣어두는 것이 좋습니다. 브라우저가 intrinsic aspect ratio를 계산하는 데 사용합니다. Next.js Image 컴포넌트와의 선택 기준은 img vs Image 비교를 참고하세요.
5Preload의 함정
“중요한 이미지는 preload하면 더 빠르겠지?” — 맞습니다. 하지만 LCP 이미지 1개에만 적용해야 합니다.
// layout.tsx <head>에 LCP 이미지만 preload
<link
rel="preload"
href="/images/hero.avif"
as="image"
type="image/avif"
/>Next.js의 자동 preload 주의
Next.js는 loading="lazy"가 없는 img 태그를 발견하면 자동으로 preload 태그를 생성합니다. 아래 fold 이미지에 lazy를 빼먹으면 의도치 않은 preload가 추가되어, Hero 이미지와 대역폭을 놓고 경쟁하게 됩니다. 결과적으로 LCP가 오히려 느려집니다.
실제로 아래 fold 이미지 3장에 lazy를 빼먹었을 때 preload가 4개(Hero + 3장)나 생성되어 있었습니다. lazy를 추가하고 preload를 Hero 1개만 남기자 LCP가 개선되었습니다.
6결과
총 용량
36MB → 0.8MB
97.8% 감소
LCP
110.2초 → 6.8초
93.8% 개선
Desktop 점수
52 → 80
+28점 상승
| 지표 | 변경 전 | 변경 후 |
|---|---|---|
| Mobile Performance | 38 | 45 |
| Desktop Performance | 52 | 80 |
| LCP (Mobile) | 110.2초 | 6.8초 |
| Speed Index (Mobile) | 46.4초 | 6.3초 |
| CLS | 0.500 | 0.481 |
Mobile은 왜 7점만 올랐나?
이미지를 97% 줄였는데 Mobile은 7점밖에 안 올랐습니다. CLS가 0.481로 여전히 심각했기 때문입니다. Lighthouse는 CLS에 전체 점수의 25%를 배정합니다. CLS를 수정한 다음 단계에서 Mobile이 45 → 70으로 25점이 올랐습니다.
이 글의 핵심 정리
- ✓이미지 최적화는 성능 개선의 첫 번째 단계 — 가장 쉽고 효과가 큼
- ✓sharp로 WebP quality 80, 최대 너비 1920px 리사이즈면 97% 이상 감소
- ✓Hero 이미지는 AVIF + fetchPriority="high", 나머지는 WebP + loading="lazy"
- ✓preload는 LCP 이미지 1개에만 — 여러 개 넣으면 오히려 역효과
- ✓width/height 속성으로 CLS 방지 — CSS 크기 제어와 별개로 HTML 속성 필수
본 데이터는 2026년 2월에 측정되었습니다. 이미지 포맷 지원 현황은 브라우저 버전에 따라 달라질 수 있습니다. sharp의 압축 결과는 원본 이미지 특성(색상 복잡도, 디테일 수준)에 따라 차이가 있을 수 있습니다. 본 콘텐츠의 비상업적 공유는 자유이나, 상업적 이용 시 문의 페이지를 통해 연락 바랍니다.
댓글
(4개)로그인하면 댓글을 작성할 수 있습니다.
preload를 아래 fold 이미지에 넣으면 오히려 LCP가 느려진다는 거, 경험으로 알긴 했는데 왜 그런지 이제 이해했습니다. 대역폭 경쟁 때문이었군요.
AVIF가 40~60% 더 작지만 인코딩이 6배 느리다는 비교가 유용합니다. 빌드 시간도 고려해야 하니까요.
fetchPriority=high랑 loading=lazy 전략이 실용적이네요. 그냥 다 lazy 넣으면 되는 줄 알았는데 Hero 이미지는 반대로 해야 한다는 게 포인트.
관련 글
© 2026 TreeRU. All rights reserved.
본 콘텐츠의 저작권은 TreeRU에 있으며, 출처를 밝히지 않은 무단 전재 및 재배포를 금합니다. 인용 시 출처(treeru.com)를 반드시 명시해 주세요.