이미지 최적화: 이미지 용량 97% 감소
srcset + CDN 리사이징으로 773kB → 20kB
목차
- 렌더링 전략 최종 선택: Streaming
- 이미지 최적화: 이미지 용량 97% 감소 ← 현재 문서
- 무한 스크롤 구현 및 리렌더링 최적화
- 에러 방어: 무한 재요청 방지, 빈 목록 처리
이 단계의 목표
게시글 카드에 표시되는 이미지가 원본 크기 그대로 전송되고 있었습니다. 화면에 보이는 크기보다 훨씬 큰 이미지를 다운로드하면 불필요한 트래픽이 발생하고 페이지 로딩이 느려집니다.
이미지를 화면에 실제로 표시되는 크기에 맞춰 전송하여, 불필요한 다운로드를 제거하는 것이 이 단계의 목표입니다.
이 단계의 구현 코드는 GitHub에서 확인할 수 있습니다.
1. 문제 정의
게시글 카드 너비가 300~450px 수준인데, 원본 크기 이미지를 그대로 요청하고 있었습니다.
Cloudinary URL에 변환 파라미터(w_{size},c_fill,f_auto)를 붙이면 리사이즈된 WebP 이미지를 받을 수 있으므로, 프론트엔드에서 적절한 크기를 결정하여 요청하도록 구현합니다.
2. 선택지 비교
프론트엔드가 "몇 px 이미지를 요청할 것인가"를 결정하는 방법은 3가지가 있었습니다.
| JS 런타임 계산 | next/image | srcset + sizes | |
|---|---|---|---|
| 크기 결정 주체 | JavaScript | Next.js | 브라우저 |
| SSR 호환 | window 없어 서버에서 불가 | 가능 | 가능 |
JS 런타임 계산의 근본적 문제
이미지 크기를 JS로 결정하면, JS가 실행되어야 필요한 사이즈를 알 수 있고, 그래야 최적 URL을 조합할 수 있어 이미지 요청 시점이 늦어집니다.
next/image의 근본적 문제
- 이미지 리사이징을 프론트 서버에서 수행하여 서버 부하가 증가합니다.
- 빌드 시 리사이징된 이미지 캐시가 초기화되어 별도 대응이 필요했으나, 다른 선택지를 채택하게 되어 대응하지 않았습니다.
위 문제들이 해결되더라도 srcset + sizes 방식의 장점이 커서 진행하지 않았습니다.
3. 결정: srcset + sizes
장점
- 이미지 요청 시점: srcset은 HTML에 포함되므로, 브라우저가 HTML을 파싱하는 즉시 적절한 크기의 이미지를 요청할 수 있습니다. JS 실행이나 별도 서버 처리를 기다릴 필요가 없습니다.
- 서버 부하 없음: 리사이징을 프론트 서버가 아닌 CDN 측에서 처리하므로 프론트 서버에 부하가 없습니다.
- 캐시 관리 불필요: CDN이 이미지를 캐시하므로 빌드나 배포와 무관하게 캐시가 유지됩니다.
단점
- sizes를 문자열로 직접 작성해야 함:
sizes="(max-width: 1023px) 50vw, 25vw"처럼 미디어쿼리 기반 문자열을 레이아웃에 맞게 수동으로 작성해야 합니다. - 다만 next/image도 유사한 불편함이 있습니다:
width/height를 props로 지정해야 하지만, 실제 화면에 렌더링되는 크기는 CSS가 결정하므로 이미지 크기를 props와 CSS 두 곳에서 따로 관리해야 합니다.
4. 구현
컴포넌트 구조
Image.tsx
├── BaseImage — 모든 이미지 공통 (lazy loading, max-width: 100%)
└── OptimizedImage — Cloudinary URL → 리사이즈 srcset 변환
BoardCard sizes 설정
25vw
- 4열 그리드 → 이미지가 뷰포트의 약 25%
CLS 방지
기존 .imageWrapper에 aspect-ratio: 1 / 1.2가 적용되어 있어 이미지 로드 전에도 영역이 확보됩니다.
5. 결과 — 이미지 용량 97% 감소
동일 이미지, 동일 렌더링 크기(320 x 384px) 기준:
| Before | After |
|---|---|
| Before | After | |
|---|---|---|
| 원본 크기 | 1920 x 1280px | 400 x 267px |
| 파일 크기 | 773 kB | 20.2 kB |
| 포맷 | JPEG (Cloudinary 원본) | WebP (변환 파라미터) |
동일 렌더링 크기에서 약 97% 용량 감소.
6. 향후 개선 가능 사항
이번 예제에서는 이미지 최적화(리사이징 + 포맷 변환)에 집중했으며, 아래 항목은 범위에서 제외했습니다.
- 이미지 404 폴백: 게시글 이미지가 삭제되거나 URL이 유효하지 않을 경우 대체 이미지를 표시하는 기능. BaseImage에
onError핸들러를 추가하면 모든 이미지에 일괄 적용됩니다.