프론트엔드 개발자가 앱 성능을 최적화하기 위해 이미지 처리 방식에 대해 고민해야 경우가 많습니다.
Next.js의 Image
컴포넌트를 활용하면 쉽게 최적화가 가능하겠지만 Next.js를 사용하지 않을 경우 어떤 방법이 있는지 알아보겠습니다.
고해상도 이미지의 경우 크기가 커서 렌더링 속도가 느려지고 많은 이미지를 서버에 요청해 서버의 부하가 커지고 트래픽 비용이 많이 발생하는 경우 이미지 최적화가 필요합니다.
이미지 최적화 방법은 이미지 포맷 변경, 리사이징, 압축, 지연 로딩, 캐싱 등이 있습니다.
이미지 리사이징은 원본 이미지의 가로세로 픽셀 크기를 줄이거나 늘리는 작업입니다. 보통 이미지 최적화에는 크기를 줄이는 것이 일반적입니다.
이미지를 저해상도로 변환하는 것도 이미지 리사이징의 일종으로 예를 들어, 3000px x 2000px(고해상도) 이미지를 1500px x 1000px(저해상도)로 줄이면 픽셀 수가 절반으로 감소하고 용량도 작아집니다.
웹 페이지에서 200px x 200px로 표시하려는 이미지를 3000px x 3000px로 저장하는 경우 이미지를 리사이징하여 업로드하는 것이 서버의 저장 공간도 줄일 수 있고 렌더링 속도도 증가시킬 수 있습니다.
리사이징 시 주의할 점은 Aspect Ratio(종횡비)를 유지해야 합니다. 가로세로 비율을 강제로 변경하면 이미지가 찌그러지는 경우가 있기 때문에 원본 비율을 유지하면서 크기를 줄이는 것이 중요합니다.
반응형 웹의 경우 모바일, 테블릿, 데스크톱 등 화면 크기에 따라 다른 크기의 이미지를 제공해야 합니다. 이때 <img>
태그의 srcset
을 활용하여 디바이스 해상도에 맞는 크기의 이미지를 로드할 수 있습니다.
<img
src="default.jpg"
srcSet="small.jpg 480w, medium.jpg 1024w, large.jpg 1600w"
/>
위와 같이 srcset
을 사용하면 브라우저에서 나열된 이미지 중 사용자의 디바이스에 적합한 크기를 자동으로 선택해 이미지를 보여줍니다.
화면 너비가 480px ~ 1023px인 경우 small.jpg
화면 너비가 1024px ~ 1599px인 경우 medium.jpg
화면 너비가 1600px보다 큰 경우 large.jpg
sizes
속성을 사용해 브라우저가 선택 기준을 더 정확하게 이해할 수 있도록 설정할 수 있습니다.
<img
src="default.jpg"
srcSet="small.jpg 480w, medium.jpg 1024w, large.jpg 1600w"
sizes="(max-width: 600px) 480px, (max-width: 1200px) 1024px, 1600px"
/>
화면 너비가 600px 이하인 경우 이미지 크기를 480px로 로드
화면 너비가 601px ~ 1200px인 경우 이미지 크기를 1024px로 로드
그 외의 경우 이미지 크기를 1600px로 로드
이미지를 최적화하기 위해서는 적절한 이미지 포맷을 선택하는 것도 중요합니다.
주로 사용하는 이미지 포맷과 그 특징은 아래와 같습니다.
포맷 | 파일 크기 | 품질 | 압축 방식 | 투명도 지원 | 애니메이션 지원 | 주요 사용 사례 |
---|---|---|---|---|---|---|
JPEG | 작음 | 높음 (손실 압축) | 손실 압축 | ❌ | ❌ | 일반 사진, 배경 이미지 |
PNG | 큼 | 매우 높음 (무손실) | 무손실 압축 | ✅ | ❌ | 아이콘, 로고, 투명 이미지 |
GIF | 중간 | 낮음 | 무손실 (256색) | ✅ | ✅ | 간단한 애니메이션 |
WebP | 매우 작음 | 높음 (손실 & 무손실 지원) | 손실 + 무손실 | ✅ | ✅ | 모든 이미지 유형 |
AVIF | 매우 작음 | 매우 높음 | 손실 + 무손실 | ✅ | ✅ | 차세대 이미지, HDR 지원 |
SVG | 벡터 방식 | 선명함 | 벡터 그래픽 | ✅ | ✅ | 아이콘, 로고, 그래픽 |
BMP | 매우 큼 | 매우 높음 | 무손실 | ✅ | ❌ | 잘 사용되지 않음 |
TIFF | 매우 큼 | 매우 높음 | 무손실 | ✅ | ❌ | 인쇄용 이미지 |
일반적으로 웹에서 BMP, TIFF는 사용하지 않습니다.
JPEG
vsWebP
vsAVIF
JPEG는 기본적인 사진 포맷으로 용량이 작고 빠르게 로드되지만 손실 압축이 있어 품질 저하의 가능성이 있습니다.
WebP는 JPEG보다 30%~50% 더 작은 크기로 압축 가능하며 화질 차이가 거의 없습니다.
AVIF는 WebP보다 더 뛰어난 압축률과 화질을 제공하지만 지원하는 브라우저가 적습니다.
일반 이미지 포맷으로는 WebP가 권장됩니다.
PNG
vsWebP
vsAVIF
PNG는 무손실 압축이지만 용량이 매우 큽니다.
WebP는 PNG보다 약 26% 더 작은 용량으로 투명도를 유지할 수 있습니다.
AVIF는 WebP보다 더 작은 용량에 뛰어난 화질을 지원합니다.
투명한 이미지 포맷으로도 WebP가 권장됩니다.
GIF
vsWebP
vsAVIF
GIF는 256색 제한에 용량이 크고 화질이 낮습니다.
WebP는 애니메이션을 지원하고 GIF에 비해서 용량이 약 30% 이상 작습니다.
AVIF는 마찬가지로 WebP보다 더 좋은 압축률과 고화질을 지원합니다.
애니메이션 이미지 포맷으로도 WebP가 권장됩니다.
SVG
vsPNG
vsWebP
SVG는 크기에 관계없이 품질 저하가 없고 코드로 표현이 가능합니다.
벡터가 아닌 경우에는 WebP 사용을 권장합니다.
벡터 그래픽에는 SVG를 권장합니다.
WebP, AVIF는 최신 포맷으로 WebP는 거의 모든 최신 브라우저에서 지원되지만 AVIF는 Safari 및 일부 브라우저에서는 아직 지원되지 않습니다.
브라우저마다 지원하는 포맷이 다르므로, HTML의 <picture>
태그를 사용하여 브라우저에 맞는 포맷을 로드할 수 있습니다.
<picture>
<source srcSet="image.avif" type="image/avif" />
<source srcSet="image.webp" type="image/webp" />
<img src="image.jpg" />
</picture>
AVIF를 지원하는 경우 image.avif
로드
AVIF를 지원하지 않지만 WebP를 지원하는 경우 image.webp
로드
WebP, AVIF 모두 지원하지 않는 경우 image.jpg
로드
페이지에 많은 이미지가 필요하거나 사용자가 화면을 스크롤해야 이미지를 볼 수 있을 경우 지연 로딩을 사용해 최적화할 수 있습니다.
지연 로딩은 페이지가 로드될 때 즉시 이미지를 불러오는 것이 아니라 사용자가 해당 이미지를 필요로 할 때(스크롤)에 로딩하는 기법입니다.
지연 로딩을 사용하면 불필요한 리소스를 다운로드를 줄여 초기에 페이지를 빠르게 표시할 수 있고 네트워크 트래픽을 감소시킬 수 있습니다.
<img src="image.jpg" loading="lazy" />
위와 같이 loading
속성을 lazy
로 설정하면 브라우저가 자동으로 사용자가 화면에 근접했을 때 이미지를 로드합니다.
만약 사용자가 빠르게 스크롤을 내리고 이미지의 크기가 커서 로드가 느린 경우 빈 화면으로 보일 수 있습니다. 이 경우 저해상도 이미지를 미리 로드해서 보여준 뒤 원본 이미지를 로드해서 UX를 향상시킬 수 있습니다.
위와 같은 방식을 LQIP
라고 합니다. LQIP
는 HTML 태그만으로는 구현하기 어렵고 CSS, javascript를 함께 사용해서 구현해야 합니다.
CDN(Content Delivery Network)은 전 세계 여러 지역에 분산된 서버를 통해 콘텐츠를 빠르게 제공하는 네트워크입니다.
사용자에게 물리적으로 가장 가까운 CDN 서버에서 이미지를 제공하기 때문에 대기 시간이 감소하고 더 빠르게 페이지를 표시할 수 있습니다.
웹 사이트의 원본 서버에서 직접 이미지를 제공하는 것이 아닌 CDN 서버에서 캐싱된 이미지를 제공하기 때문에 서버 부하와 트래픽 비용을 절감할 수 있습니다.
브라우저와 CDN이 이미지 파일을 캐싱하기 때문에 동일한 이미지를 여러 페이지에서 사용하는 경우 네트워크 요청을 줄일 수 있습니다. 여기서 브라우저의 캐싱은 디스크 캐싱을 의미합니다.
현대의 많은 CDN 서비스들은 이미지 리사이징과 포맷 변환을 지원합니다. 실시간 이미지 리사이징, 포맷 변환, URL 기반으로 이미지 크기 변환을 지원하는 등 원본 이미지를 한 번만 업로드하면 URl 파라미터를 통해 다양한 크기와 포맷의 이미지를 동적으로 생성할 수 있습니다.