테마 전환

Astro 이미지 최적화 완벽 가이드: 웹사이트 로딩 속도를 50% 높이는 5가지 실전 팁

Easton editorial illustration: cache waterfall instrument

지난달 Astro로 기술 블로그를 만들고 신나게 공개했는데, 막상 열어 보니 초기 화면을 불러오는 데 무려 6초가 걸렸습니다. 이미지 한 장이 2~3MB에 달했고 모바일에서는 콘텐츠가 나타날 때까지 한참을 기다려야 했습니다. 성능이 너무 나빠 정말 당황했습니다.

이후 이틀 동안 Astro 이미지 최적화를 연구했습니다. Image 컴포넌트 설정부터 형식 선택, 지연 로딩, CDN 연동까지 하나씩 개선한 결과 초기 화면 로딩은 1.8초로 줄었고 Lighthouse 성능 점수는 62점에서 95점으로 뛰었습니다. 솔직히 95점이라는 숫자를 봤을 때 무척 뿌듯했습니다.

이 글에서는 이틀 동안 겪은 시행착오와 정리한 경험을 모두 공유합니다.

  • Astro Image 컴포넌트를 처음부터 설정하는 완전한 코드 예제
  • JPEG, PNG, WebP, AVIF 네 가지 형식 선택법
  • 지연 로딩 설정 모범 사례
  • Cloudflare CDN 연동 상세 단계
  • 자주 발생하는 문제의 진단 방법

이 글을 다 읽으면 여러분의 Astro 사이트도 로딩 속도를 50% 이상 높일 수 있습니다. 바로 시작하겠습니다.

Astro 이미지 최적화가 중요한 이유

이미지 최적화가 그다지 중요하지 않다고 생각할 수도 있습니다. 몇 초 더 기다리는 정도라고 여길 수 있기 때문입니다. 하지만 이미지는 보통 전체 웹페이지 용량의 60~70%를 차지하며, 사이트 성능을 떨어뜨리는 가장 큰 원인입니다.

예전에 운영하던 블로그에서는 압축하지 않은 표지 이미지 한 장만 2.5MB였습니다. 글에 스크린샷 몇 장을 더 넣으면 전체 페이지가 쉽게 5~6MB가 됐습니다. 사용자는 사이트를 연 뒤 이미지만 몇 초씩 기다려야 했고 이탈률도 매우 높았습니다.

Google의 엄격한 이미지 로딩 기준

Google의 Core Web Vitals에는 LCP(Largest Contentful Paint, 최대 콘텐츠 렌더링 시간)라는 지표가 있습니다. 쉽게 말하면 페이지의 주요 콘텐츠가 로딩을 마치는 데 걸리는 시간입니다. Google은 LCP가 2.5초 이내여야 한다고 보며, 4초를 넘으면 낮은 평가를 받습니다.

대부분의 사이트에서 LCP 요소는 큰 표지 이미지나 초기 화면 이미지입니다. 이미지 로딩이 느리면 LCP가 높아지고 SEO 순위에도 영향을 줍니다.

실제 최적화 효과

제 블로그의 최적화 전후 데이터를 정리해 봤습니다.

최적화 전:

  • 초기 화면 로딩 시간: 6.2초
  • Lighthouse 성능 점수: 62점
  • 전체 이미지 용량: 약 8MB
  • LCP: 4.8초
71%
로딩 시간 감소
6.2초에서 1.8초로 단축
62→95점
Lighthouse 향상
성능 점수 53% 향상
85%
이미지 용량 감소
8MB에서 1.2MB로 축소
1.8초
초기 화면 로딩 시간
6.2초에서 1.8초로 단축, Lighthouse 점수는 62점에서 95점으로 향상

최적화 후:

  • 초기 화면 로딩 시간: 1.8초(70% 개선)
  • Lighthouse 성능 점수: 95점(53% 개선)
  • 전체 이미지 용량: 약 1.2MB(85% 감소)
  • LCP: 1.3초(73% 개선)

솔직히 결과는 예상보다 훨씬 좋았습니다. 더 중요한 점은 최적화 후 사이트 이탈률이 약 35% 낮아져 사용자가 콘텐츠를 더 오래 읽게 됐다는 것입니다.

Astro는 본래 성능을 중시하는 프레임워크입니다. 이미지 최적화를 제대로 하지 않아 그 장점을 잃는다면 정말 아깝습니다. 이제 이미지 최적화를 단계별로 제대로 적용해 보겠습니다.

Astro Image 컴포넌트 완벽 가이드

Astro에는 이미지 최적화를 위한 <Image /><Picture /> 컴포넌트가 내장돼 있습니다. 처음 공식 문서를 읽었을 때는 widths, quality, inferSize 같은 매개변수가 무엇을 하는지 잘 이해되지 않았지만 직접 사용해 보고 나서야 감을 잡았습니다.

기본 사용법: Image와 Picture

먼저 가장 자주 쓰는 <Image /> 컴포넌트입니다.

---
import { Image } from 'astro:assets';
import coverImage from '../assets/blog-cover.jpg';
---

<Image
  src={coverImage}
  alt="블로그 표지 이미지"
  width={1200}
  height={630}
/>

이 컴포넌트는 다음 작업을 자동으로 수행합니다.

  • 이미지 압축
  • WebP 형식으로 변환(기본 동작)
  • 반응형 이미지 생성
  • 로딩 성능 최적화

<Picture /> 컴포넌트는 더 강력하며 여러 형식의 대체 옵션을 제공할 수 있습니다.

---
import { Picture } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---

<Picture
  src={heroImage}
  formats={['avif', 'webp', 'jpeg']}
  alt="Hero 이미지"
  width={1920}
  height={1080}
/>

브라우저는 가장 작은 AVIF를 우선 불러오고, 지원하지 않으면 WebP, 그마저 지원하지 않으면 JPEG를 사용합니다. 성능과 호환성을 모두 확보할 수 있습니다.

주요 속성 상세 설명

처음에는 이 매개변수를 어떻게 설정해야 할지 몰랐지만 몇 번 테스트하면서 기준을 찾았습니다.

widths - 반응형 너비

<Image
  src={image}
  widths={[400, 800, 1200]}
  sizes="(max-width: 768px) 400px, (max-width: 1024px) 800px, 1200px"
  alt="반응형 이미지"
/>

이렇게 설정하면 Astro가 세 가지 크기의 이미지를 생성하고 브라우저가 화면 크기에 맞는 이미지를 자동으로 선택합니다. 모바일에서는 작은 이미지를, 데스크톱에서는 큰 이미지를 불러오므로 데이터도 아끼고 속도도 빨라집니다.

quality - 품질 제어

<Image
  src={image}
  quality="mid"  // 또는 숫자 사용: quality={80}
  alt="블로그 이미지"
/>
  • low: 썸네일과 배경 이미지에 적합
  • mid: 대부분의 상황에 충분함(권장)
  • high: 화질 요구가 높은 상황에 적합
  • 숫자(0~100): 정밀 제어

저는 보통 mid80을 사용합니다. 육안으로 차이를 알아보기 어렵지만 파일 용량은 30~40% 줄어듭니다.

inferSize - 원격 이미지의 구원자

<Image
  src="https://example.com/image.jpg"
  inferSize={true}
  alt="원격 이미지"
/>

크기를 모르는 원격 이미지를 사용할 때 inferSize를 추가하면 Astro가 자동으로 가져옵니다. 실제로 여러 번 큰 도움이 됐습니다.

loading - 지연 로딩 설정

<!-- 초기 화면 이미지, 즉시 로딩 -->
<Image src={hero} loading="eager" alt="초기 화면 이미지" />

<!-- 초기 화면 아래, 지연 로딩 -->
<Image src={content} loading="lazy" alt="콘텐츠 이미지" />

format - 출력 형식

<Image
  src={image}
  format="webp"  // webp | avif | jpeg | png
  alt="지정 형식"
/>

로컬 이미지와 원격 이미지

처음에는 이 부분도 헷갈렸지만 원리는 간단합니다.

로컬 이미지(권장)

---
// src/assets/ 또는 src/images/ 디렉터리에 저장
import myImage from '../assets/photo.jpg';
---
<Image src={myImage} alt="로컬 이미지" />

로컬 이미지는 Astro가 자동으로 최적화하고 압축해 번들링하므로 이 방법을 적극 권장합니다.

원격 이미지

---
// astro.config.mjs에서 허용 도메인 설정 필요
---
<Image
  src="https://images.unsplash.com/photo-xxx"
  width={800}
  height={600}
  alt="원격 이미지"
/>

astro.config.mjs에 다음과 같이 설정해야 합니다.

export default defineConfig({
  image: {
    domains: ['images.unsplash.com', 'cdn.example.com']
  }
});

public 디렉터리의 이미지

<!-- public/logo.png → /logo.png -->
<img src="/logo.png" alt="Logo" />

public/ 디렉터리의 이미지는 최적화되지 않으므로 Logo나 favicon 같은 작은 파일에만 적합합니다.

실전 코드 예제

다음은 제 블로그에서 실제로 사용하는 설정입니다.

블로그 표지 이미지(초기 화면, 미리 로딩)

---
import { Image } from 'astro:assets';
import coverImage from '../assets/blog-cover.jpg';
---

<Image
  src={coverImage}
  alt="Astro 이미지 최적화 완벽 가이드"
  width={1200}
  height={630}
  format="webp"
  quality={85}
  loading="eager"
  class="blog-cover"
/>

본문 이미지(지연 로딩)

<Image
  src={screenshot}
  alt="설정 예제 스크린샷"
  width={800}
  height={450}
  format="webp"
  quality="mid"
  loading="lazy"
/>

작성자 프로필 이미지(작은 아이콘, 미리 로딩)

<Image
  src={avatar}
  alt="작성자 프로필 이미지"
  width={48}
  height={48}
  format="webp"
  loading="eager"
/>

Image 컴포넌트 설정을 살펴봤으니 이제 많은 사람이 고민하는 이미지 형식 선택법을 알아보겠습니다.

이미지 형식 선택 완벽 가이드

JPEG, PNG, WebP, AVIF처럼 형식이 많아 무엇을 골라야 할지 고민될 수 있습니다. 저도 처음에는 막막했지만 직접 테스트한 뒤 각 형식에 맞는 용도를 파악했습니다.

주요 네 가지 형식 비교

먼저 표로 비교해 보겠습니다.

형식압축 유형파일 용량브라우저 지원용도
JPEG손실중간100%사진, 복잡한 이미지
PNG무손실100%투명도가 필요한 이미지
WebP손실/무손실작음97%+일반적인 용도(권장)
AVIF손실/무손실가장 작음90%+극한의 압축이 필요한 경우

JPEG - 오래된 형식으로 호환성이 가장 좋음

  • 장점: 모든 브라우저에서 지원하며 압축률도 준수함
  • 단점: 투명도를 지원하지 않고 WebP보다 압축률이 낮음
  • 용도: 블로그 이미지, 제품 이미지, 인물 사진

PNG - 무손실 압축과 투명도 지원

  • 장점: 화질 손실이 없고 투명도를 지원함
  • 단점: 파일이 크며 보통 JPEG의 2~3배
  • 용도: Logo, 아이콘, 투명 배경이 필요한 이미지

WebP - Google이 적극 권장하며 균형이 가장 좋음

  • 장점: JPEG보다 압축률이 30% 높고 투명도를 지원하며 호환성이 좋음
  • 단점: 극히 일부 오래된 브라우저는 지원하지 않음(하지만 이미 2025년입니다)
  • 용도: 대부분의 상황에서 제일 먼저 선택하는 형식

AVIF - 최신 형식으로 압축률이 가장 높음

  • 장점: WebP보다 압축률이 20~30% 더 높고 화질도 좋음
  • 단점: 브라우저 지원률이 조금 낮고 인코딩이 느림
  • 용도: 매우 높은 성능이 필요한 상황

형식 선택 의사 결정 트리

매개변수를 모두 보기 싫다면 다음 흐름대로 선택하면 됩니다.

투명도가 필요한가?
├─ 예 → WebP(우선) 또는 PNG(대체)
└─ 아니요 → 계속

사진이나 복잡한 이미지인가?
├─ 예 → WebP(우선) 또는 AVIF(극한의 최적화)
└─ 아니요 → 계속

Logo나 아이콘인가?
├─ 예 → SVG(벡터 이미지)
└─ 아니요 → 계속

움직이는 이미지인가?
└─ WebP(GIF 대체)

제 경험으로는 90%의 상황에서 WebP를 바로 사용하면 됩니다. 호환성과 압축률이 모두 좋아 편리합니다.

형식 호환성 처리

AVIF를 사용하고 싶지만 호환성이 걱정된다면 <Picture> 컴포넌트로 대체 형식을 제공하세요.

---
import { Picture } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---

<Picture
  src={heroImage}
  formats={['avif', 'webp', 'jpeg']}
  alt="Hero 이미지"
  width={1920}
  height={1080}
/>

브라우저의 로딩 순서는 다음과 같습니다.

  1. 가장 작고 최신인 AVIF를 먼저 시도합니다.
  2. 지원하지 않으면 더 작고 호환성이 좋은 WebP를 사용합니다.
  3. 둘 다 안 되면 크지만 호환성이 100%인 JPEG를 사용합니다.

최신 브라우저에는 최상의 경험을 제공하면서도 오래된 브라우저에서 이미지가 보이지 않는 문제를 피할 수 있습니다.

압축률 실제 비교

2.5MB 원본 이미지로 테스트했습니다.

형식파일 용량압축률시각적 품질
원본(PNG)2.5MB-원본
JPEG(quality=85)450KB82%육안으로 거의 차이 없음
WebP(quality=85)180KB93%육안으로 거의 차이 없음
AVIF(quality=85)120KB95%육안으로 거의 차이 없음

같은 시각적 품질에서 WebP는 JPEG보다 60% 작고 AVIF는 73%나 작습니다. 압축 효과가 매우 뛰어납니다.

권장 사항:

  • 일반적인 용도: 호환성과 압축률이 모두 좋은 WebP
  • 극한의 최적화: AVIF + WebP + JPEG 3단계 대체
  • 투명도 필요: WebP(우선) 또는 PNG(대체)
  • Logo와 아이콘: 가능하다면 손실 없이 확대할 수 있는 SVG 사용

형식을 선택했으니 이제 초기 화면 로딩 속도를 크게 개선할 수 있는 지연 로딩을 알아보겠습니다.

지연 로딩 설정 모범 사례

지연 로딩(Lazy Loading)은 말 그대로 필요할 때 이미지를 불러오는 방식입니다. 뷰포트 밖에 있는 이미지는 먼저 불러오지 않고 사용자가 가까이 스크롤했을 때 로딩을 시작하므로 초기 화면 로딩 시간을 크게 줄일 수 있습니다.

지연 로딩 원리

최신 브라우저는 loading 속성만으로 기본 지연 로딩을 지원합니다.

<img src="image.jpg" loading="lazy" alt="지연 로딩 이미지" />

Astro Image 컴포넌트는 기본적으로 지연 로딩이 활성화돼 있어 별도 설정이 필요하지 않습니다. 정밀하게 제어하려면 다음과 같이 설정합니다.

<!-- 지연 로딩(기본값) -->
<Image src={image} loading="lazy" alt="지연 로딩" />

<!-- 즉시 로딩 -->
<Image src={image} loading="eager" alt="즉시 로딩" />

브라우저는 Intersection Observer API로 이미지가 뷰포트에 들어오는지 감시하다가 가까워졌을 때 로딩을 시작합니다.

지연 로딩 설정 전략

핵심은 어떤 이미지를 지연 로딩하고 어떤 이미지를 즉시 로딩할지 판단하는 것입니다.

즉시 로딩(loading="eager"):

  • 초기 화면에 보이는 이미지(Hero 이미지, 표지 이미지)
  • Logo, 내비게이션 바 아이콘
  • 핵심 비즈니스 이미지(제품 대표 이미지, 프로필 이미지)
  • Above the fold(초기 화면 영역)의 모든 콘텐츠

지연 로딩(loading="lazy"):

  • 초기 화면 아래의 콘텐츠 이미지
  • 글 안의 이미지와 스크린샷
  • 목록 페이지의 썸네일
  • 푸터 이미지
  • 장식용 이미지

제 경험으로는 초기 화면 이미지 1~2장만 즉시 불러오면 충분하며, 나머지는 모두 지연 로딩하면 됩니다. 초기 화면을 빠르게 표시하면서 한꺼번에 너무 많은 이미지를 불러오지 않을 수 있습니다.

실전 설정 예제

다음은 제 블로그에서 실제로 사용하는 설정입니다.

블로그 홈

---
import { Image } from 'astro:assets';
---

<!-- Hero 표지 이미지, 즉시 로딩 -->
<Image
  src={heroCover}
  loading="eager"
  alt="블로그 홈 표지"
  width={1920}
  height={1080}
/>

<!-- 글 목록 썸네일, 지연 로딩 -->
{posts.map(post => (
  <Image
    src={post.thumbnail}
    loading="lazy"
    alt={post.title}
    width={400}
    height={225}
  />
))}

글 상세 페이지

<!-- 글 상단 이미지, 즉시 로딩 -->
<Image
  src={article.cover}
  loading="eager"
  alt={article.title}
  width={1200}
  height={630}
/>

<!-- 본문 이미지, 모두 지연 로딩 -->
<Image
  src={screenshot1}
  loading="lazy"
  alt="코드 예제 스크린샷"
  width={800}
  height={450}
/>

<Image
  src={screenshot2}
  loading="lazy"
  alt="효과 비교 이미지"
  width={800}
  height={450}
/>

이미지 갤러리(특수한 상황)

<!-- 첫 이미지 묶음은 즉시 로딩 -->
{gallery.slice(0, 6).map(img => (
  <Image src={img} loading="eager" alt={img.alt} />
))}

<!-- 이후 이미지는 지연 로딩 -->
{gallery.slice(6).map(img => (
  <Image src={img} loading="lazy" alt={img.alt} />
))}

성능 모니터링

최적화 후 효과는 다음 도구로 확인할 수 있습니다.

1. Lighthouse(Chrome DevTools)

F12를 눌러 개발자 도구를 열고 Lighthouse 패널에서 “Analyze page load”를 클릭합니다.

  • Performance 점수: 90점 이상이어야 함
  • LCP: 2.5초 이내여야 함
  • CLS: 레이아웃 이동을 피하려면 0에 가까워야 함

최적화 전에는 Performance가 62점, LCP가 4.8초였지만 최적화 후에는 Performance가 95점, LCP가 1.3초로 개선됐습니다.

2. Chrome DevTools Network 패널

F12 → Network → “Disable cache” 선택 → 페이지 새로고침:

  • 워터폴 차트에서 이미지 로딩 순서가 적절한지 확인합니다.
  • 초기 화면 이미지가 우선 로딩되는지 확인합니다.
  • 지연 로딩 이미지가 스크롤할 때 로딩되는지 확인합니다.

3. WebPageTest

실제 사용자 환경의 성능을 확인하려면 WebPageTest로 여러 지역과 기기의 로딩 성능을 테스트할 수 있습니다.

최적화 전후 비교:

지표최적화 전최적화 후개선
초기 화면 로딩 시간6.2초1.8초71%
LCP4.8초1.3초73%
초기 화면 이미지 총용량8MB1.2MB85%
Performance 점수62점95점53%

개선 효과가 매우 뚜렷하며 사용자 경험도 완전히 달라집니다.

지연 로딩 설정을 마쳤으니 이제 CDN을 연동해 전 세계 접속 속도를 더 높여 보겠습니다.

이미지 CDN 연동 실전

처음에는 설정이 복잡하고 비용이 많이 들까 봐 CDN을 도입할지 고민했습니다. 하지만 Cloudflare 무료 한도로도 충분하고 설정도 어렵지 않다는 것을 알고 바로 연동했습니다.

CDN이 필요한 이유

CDN(Content Delivery Network)은 이미지를 전 세계 노드에 캐시하고 사용자가 접속할 때 가장 가까운 노드에서 불러오는 방식입니다.

CDN의 장점:

  • 전 세계 가속: 베이징 사용자는 베이징 노드, 뉴욕 사용자는 뉴욕 노드에서 로딩
  • 서버 부하 감소: 이미지 요청을 모두 CDN이 처리하므로 원본 서버의 부담이 크게 줄어듦
  • 자동 최적화: 많은 CDN이 이미지 형식 변환과 압축을 자동으로 수행
  • 장애 대비: 노드 하나에 장애가 생겨도 다른 노드 사용 가능

제 블로그는 Cloudflare CDN 연동 후 해외 사용자의 로딩 속도가 약 60% 개선됐습니다.

Cloudflare Image Resizing 연동

Cloudflare의 Image Resizing 서비스는 Astro와 함께 사용하기 좋습니다.

1단계: Cloudflare Image Resizing 활성화

Cloudflare Dashboard 로그인 → 도메인 선택 → Speed → Optimization → “Image Resizing” 활성화

무료 플랜은 매월 5만 회 변환을 제공하므로 개인 블로그에는 충분합니다.

2단계: astro.config.mjs 설정

import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server', // 또는 'hybrid'
  adapter: cloudflare({
    imageService: 'cloudflare' // Cloudflare 이미지 서비스 사용
  }),
  image: {
    // 원격 이미지 사용 시 허용 도메인 설정
    domains: ['images.unsplash.com', 'cdn.example.com']
  }
});

3단계: 도메인 허용 설정(원격 이미지 사용 시)

이미지가 외부 CDN에 있다면 image.domains에 허용 도메인을 추가해야 합니다.

export default defineConfig({
  image: {
    domains: [
      'images.unsplash.com',
      'cdn.example.com',
      'res.cloudinary.com'
    ]
  }
});

설정을 마치면 Astro Image 컴포넌트가 자동으로 Cloudflare 이미지 서비스를 이용해 최적화합니다.

다른 CDN 선택지

Cloudflare 외에도 다음과 같은 서비스가 있습니다.

Cloudinary

Astro SDK를 제공해 편리하게 사용할 수 있는 전문 이미지 CDN입니다.

npm install @cloudinary/url-gen
---
import { CldImage } from 'astro-cloudinary';
---

<CldImage
  src="sample"
  width={800}
  height={600}
  alt="Cloudinary 이미지"
/>

이미지 변환, 필터, 워터마크 등 강력한 기능이 장점입니다. 무료 한도가 제한적이며 초과 사용량은 유료라는 단점이 있습니다.

Uploadcare

업로드와 처리가 편리한 전문 이미지 CDN입니다.

// astro.config.mjs
export default defineConfig({
  image: {
    service: {
      entrypoint: 'uploadcare-astro',
      config: {
        publicKey: 'your-public-key'
      }
    }
  }
});

Cloudflare R2

이미지가 아주 많다면 Cloudflare R2를 객체 스토리지로 사용할 수 있습니다.

// astro.config.mjs
export default defineConfig({
  build: {
    assetsPrefix: 'https://your-r2-domain.com'
  }
});

R2는 트래픽 비용 없이 저장 용량에 대해서만 과금하므로 비용 효율이 높습니다.

CDN 설정 시 주의사항

제가 직접 겪었던 문제들이므로 주의하세요.

1. SSR 모드에서는 도메인별로 최적화를 활성화해야 함

SSR(Server-Side Rendering)을 사용한다면 Cloudflare Dashboard에서 각 도메인의 Image Resizing을 활성화해야 합니다.

2. 원격 이미지 도메인은 반드시 허용해야 함

설정하지 않으면 다음 오류가 발생합니다.

Image's component src parameter is not allowed for this image.

해결 방법은 astro.config.mjsimage.domains에 해당 도메인을 추가하는 것입니다.

3. compile 모드는 빌드할 때만 최적화함

adapter: cloudflare({
  imageService: 'compile' // 빌드할 때만 최적화
})

이 모드에서는 패키징할 때 이미지를 한 번 최적화하고 런타임에는 다시 최적화하지 않습니다. 순수 정적 사이트에 적합합니다.

4. 무료 한도 확인

CDN 서비스무료 한도초과 비용
Cloudflare Image Resizing월 5만 회5만 회당 $5
Cloudinary월 25 credits사용량에 따라 과금
Uploadcare스토리지 3GB + 트래픽 3GB사용량에 따라 과금
Cloudflare R2스토리지 10GB월 $0.015/GB

개인 블로그에는 무료 한도로도 대체로 충분하지만 상업 프로젝트라면 비용을 확인해야 합니다.

비용 비교 사례:

제 블로그는 월 방문자 약 2만 명, 이미지 요청 약 10만 회입니다.

  • Cloudflare: 무료(5만 회 한도 내)
  • Cloudinary: 유료 필요(월 $9부터)
  • Uploadcare: 유료 필요(월 $25부터)

그래서 비용이 들지 않고 사용하기도 편한 Cloudflare를 선택했습니다.

CDN 설정을 마쳤으니 마지막으로 제가 겪었던 자주 발생하는 문제를 진단하는 방법을 살펴보겠습니다.

자주 발생하는 문제와 해결 방법

이 부분에는 제가 겪었던 시행착오를 모두 정리했습니다. 여러분은 같은 문제를 덜 겪기를 바랍니다.

이미지가 표시되지 않음

문제: 페이지의 이미지 영역이 비어 있거나 깨진 이미지 아이콘이 표시됩니다.

가능한 원인과 해결 방법:

1. import 경로가 잘못됨

// ❌ 잘못된 예: 상대 경로가 틀림
import image from './assets/photo.jpg';

// ✅ 올바른 예: 경로 깊이 확인
import image from '../assets/photo.jpg';

2. 원격 이미지 도메인을 허용하지 않음

// astro.config.mjs
export default defineConfig({
  image: {
    domains: ['images.unsplash.com'] // 반드시 추가
  }
});

일반적으로 다음 오류가 표시됩니다.

Image's component src parameter is not allowed for this image.

3. 지원하지 않는 이미지 형식

Astro가 지원하는 형식은 JPG, JPEG, PNG, WEBP, AVIF, GIF, SVG입니다.

TIFF나 BMP 형식이라면 먼저 변환해야 합니다.

이미지가 흐리거나 품질이 낮음

문제: 이미지가 흐릿하게 보이거나 품질이 눈에 띄게 낮아집니다.

해결 방법:

1. quality 매개변수 조정

<!-- 품질이 너무 낮음 -->
<Image src={img} quality="low" alt="너무 흐린 이미지" />

<!-- 품질 높이기 -->
<Image src={img} quality={85} alt="더 선명한 이미지" />

2. 원본 이미지 해상도가 부족함

원본이 400x300인데 1200x900으로 표시하면 당연히 흐려집니다. 더 높은 해상도의 원본을 사용하세요.

3. 반응형 크기 설정이 부적절함

<!-- ❌ 설정 크기가 너무 작음 -->
<Image
  src={img}
  widths={[200, 400]}
  sizes="(max-width: 1920px) 400px"
  alt="데스크톱에서 흐려짐"
/>

<!-- ✅ 충분한 크기 제공 -->
<Image
  src={img}
  widths={[400, 800, 1200, 1920]}
  sizes="(max-width: 768px) 400px, (max-width: 1024px) 800px, 1200px"
  alt="모든 화면에서 선명함"
/>

빌드 오류

문제: npm run build를 실행할 때 이미지 관련 오류가 발생합니다.

1. Sharp 설치 실패

오류 메시지:

Error: Could not load the "sharp" module

해결 방법:

# node_modules를 삭제한 뒤 다시 설치
rm -rf node_modules package-lock.json
npm install

# 또는 sharp만 다시 설치
npm uninstall sharp
npm install sharp

그래도 해결되지 않으면 특정 버전을 설치해 보세요.

npm install [email protected]

2. 메모리 부족

오류 메시지:

FATAL ERROR: Reached heap limit Allocation failed

해결 방법: Node.js 메모리 제한을 늘립니다.

# package.json
{
  "scripts": {
    "build": "NODE_OPTIONS='--max-old-space-size=4096' astro build"
  }
}

3. 지원하지 않는 이미지 형식

이미지가 HEIC나 TIFF 형식이면 Sharp가 처리하지 못할 수 있습니다. 먼저 JPG나 PNG로 변환하세요.

SSR 모드 이미지 문제

문제: 로컬 개발 환경에서는 정상인데 Cloudflare Pages/Workers에 배포하면 이미지가 표시되지 않습니다.

해결 방법:

1. 올바른 imageService 설정

// astro.config.mjs
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'server',
  adapter: cloudflare({
    imageService: 'cloudflare' // 핵심 설정
  })
});

2. output 모드 확인

export default defineConfig({
  output: 'server', // 또는 'hybrid'
  // output: 'static'은 Cloudflare imageService를 지원하지 않음
});

3. 로컬 이미지 경로 문제

SSR 모드에서는 로컬 이미지를 public/이 아니라 src/ 디렉터리에 넣어야 합니다.

---
// ✅ 올바른 예: src/assets/
import image from '../assets/photo.jpg';

// ❌ 잘못된 예: public/ 이미지는 최적화되지 않음
// <img src="/photo.jpg" />
---

<Image src={image} alt="올바른 방법" />

문제 해결 체크리스트

문제가 생기면 다음 순서대로 확인하세요.

  1. ✓ import 경로가 올바른지 확인
  2. ✓ 이미지 형식이 지원되는지 확인
  3. ✓ 원격 이미지 도메인이 허용 목록에 있는지 확인
  4. ✓ 품질 매개변수가 적절한지 확인
  5. ✓ Sharp가 올바르게 설치됐는지 확인
  6. ✓ SSR 모드 설정이 올바른지 확인
  7. ✓ 브라우저 Console에 오류가 있는지 확인
  8. ✓ Network 패널에서 이미지 요청 상태 확인

이제 내용을 정리해 보겠습니다.

결론

이틀 동안 Astro 이미지 최적화를 연구하며 얻은 경험이 같은 문제를 겪는 분들에게 도움이 되기를 바랍니다.

핵심 내용을 다시 정리하면 다음과 같습니다.

  1. Image 컴포넌트 설정: <img> 태그 대신 <Image /> 컴포넌트를 사용해 압축, 형식 변환, 반응형 처리를 자동화합니다.
  2. 형식 선택: 90%의 상황에서는 WebP면 충분합니다. 극한의 최적화가 필요하면 AVIF 대체 방식을 사용하고, 투명도가 필요하면 WebP 또는 PNG를 선택합니다.
  3. 지연 로딩 전략: 초기 화면 이미지 1~2장은 즉시 로딩하고 나머지는 모두 지연 로딩해 초기 로딩 용량을 50% 이상 줄입니다.
  4. CDN 연동: 무료 한도로 충분한 Cloudflare CDN을 연동하면 전 세계 접속 속도를 60% 높일 수 있습니다.
  5. 문제 해결: 체크리스트 순서대로 확인하면 90%의 문제는 경로, 설정, Sharp 설치에서 원인을 찾을 수 있습니다.

제 블로그는 최적화 후 초기 화면 로딩이 6.2초에서 1.8초로 줄고 Lighthouse 점수가 62점에서 95점으로 올랐으며 이탈률도 35% 낮아졌습니다. 투자 대비 효과가 정말 컸습니다.

지금 바로 다음 작업을 해 보세요:

  • 사이트를 열고 F12를 눌러 Lighthouse 테스트를 실행해 현재 성능 점수를 확인합니다.
  • 이미지 형식을 확인하고 WebP로 바꿀 수 있는 이미지는 모두 변환합니다.
  • 초기 화면 밖의 이미지에 loading="lazy"를 추가합니다.
  • 이미지가 많다면 Cloudflare CDN 연동을 검토합니다.

이미지 최적화는 지속적인 과정이므로 한 번에 끝낼 필요가 없습니다. 단계별로 조금씩 최적화할 때마다 성능과 사용자 경험이 좋아집니다.

최적화 후 문제가 생기거나 더 좋은 경험이 있다면 댓글로 공유해 주세요. 여러분의 사이트가 더욱 빠르게 동작하기를 바랍니다!

Astro 이미지 최적화 완벽 가이드: 웹사이트 로딩 속도를 50% 높이는 방법

5가지 실전 팁으로 초기 화면 로딩을 6초에서 1.8초로 줄이고 Lighthouse 점수를 62점에서 95점으로 높입니다.

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: 이미지 최적화의 중요성과 목표 이해하기

    이미지 최적화가 중요한 이유:
    • 이미지는 보통 전체 웹페이지 용량의 60~70%를 차지하며 사이트 성능을 떨어뜨리는 가장 큰 원인입니다.
    • 예전에 운영하던 블로그에서는 압축하지 않은 표지 이미지 한 장만 2.5MB였습니다.
    • 글에 스크린샷 몇 장을 더 넣으면 전체 페이지가 쉽게 5~6MB가 됐습니다.
    • 사용자는 사이트를 연 뒤 이미지만 몇 초씩 기다려야 했고 이탈률도 매우 높았습니다.

    Google의 이미지 로딩 기준:
    • Core Web Vitals에는 LCP(Largest Contentful Paint, 최대 콘텐츠 렌더링 시간)라는 지표가 있습니다.
    • 쉽게 말하면 페이지의 주요 콘텐츠가 로딩을 마치는 데 걸리는 시간입니다.
    • Google은 LCP가 2.5초 이내여야 한다고 보며, 4초를 넘으면 낮은 평가를 받습니다.
    • 대부분의 사이트에서 LCP 요소는 큰 표지 이미지나 초기 화면 이미지입니다.
    • 이미지 로딩이 느리면 LCP가 높아지고 SEO 순위에도 영향을 줍니다.

    최적화 목표:
    • 초기 화면 로딩 시간을 6.2초에서 1.8초로 단축(71% 개선)
    • Lighthouse 성능 점수를 62점에서 95점으로 향상(53% 개선)
    • 전체 이미지 용량을 약 8MB에서 약 1.2MB로 축소(85% 감소)
    • LCP를 4.8초에서 1.2초로 단축(75% 개선)
  2. 2

    Step 2: 팁 1: Astro Image 컴포넌트 사용하기

    Astro Image 컴포넌트의 장점:
    • 이미지 형식, 크기, 지연 로딩을 자동으로 최적화합니다.
    • <img> 태그 대신 <Image /> 컴포넌트를 사용합니다.
    • 압축, 형식 변환, 반응형 처리를 자동으로 수행합니다.

    설정 단계:
    1. @astrojs/image 통합 패키지를 설치합니다(npx astro add image 실행).
    2. astro.config.mjs에서 imageService를 설정합니다(sharp 또는 squoosh 선택).
    3. Image 컴포넌트를 사용합니다:
    import { Image } from 'astro:assets';
    <Image src={image} alt="설명" />

    로컬 이미지 경로 문제:
    • SSR 모드에서는 로컬 이미지를 public/이 아니라 src/ 디렉터리에 넣어야 합니다.
    • 올바른 방법: import image from '../assets/photo.jpg'; <Image src={image} alt="올바른 방법" />
    • 잘못된 방법: public/의 이미지는 최적화되지 않습니다.
  3. 3

    Step 3: 팁 2~3: 적절한 이미지 형식 선택과 지연 로딩 설정

    형식 선택:
    • JPEG: 사진과 복잡한 이미지에 적합하며 호환성이 가장 좋습니다.
    • PNG: 아이콘과 투명 배경에 적합하지만 파일이 큽니다.
    • WebP: JPEG보다 30~50% 작고 최신 브라우저가 모두 지원하므로 90%의 상황에서는 WebP면 충분합니다.
    • AVIF: WebP보다 20~30% 작지만 호환성이 조금 낮습니다. 극한의 최적화가 필요할 때 대체 형식과 함께 사용합니다.

    지연 로딩 설정:
    • 초기 화면의 이미지 1~2장은 즉시 로딩하고 나머지는 모두 지연 로딩합니다.
    • 초기 로딩 용량을 50% 이상 줄일 수 있습니다.
    • loading="lazy" 속성이나 Image 컴포넌트의 loading 속성을 사용합니다.
  4. 4

    Step 4: 팁 4~5: CDN 가속과 이미지 크기 최적화

    Cloudflare CDN 연동:
    • Cloudflare Images 또는 R2 스토리지를 사용합니다.
    • 자동 최적화와 형식 변환을 설정합니다.
    • 전 세계 300개 이상의 노드에서 무료로 가속합니다.
    • 실제 테스트에서 지연 시간이 3분의 1로 줄었습니다.

    설정 단계:
    1. Cloudflare Dashboard에서 Images 또는 R2를 활성화합니다.
    2. Cloudflare를 사용하도록 imageService를 설정합니다.
    3. 이미지를 Cloudflare에 업로드합니다.
    4. Cloudflare URL로 이미지를 불러옵니다.

    이미지 크기 최적화:
    • srcset과 sizes 속성으로 기기에 맞는 크기의 이미지를 불러옵니다.
    • Image 컴포넌트의 width와 height 속성으로 적절한 이미지 크기를 지정합니다.
  5. 5

    Step 5: 문제 해결과 모범 사례

    문제 해결 체크리스트:
    1. import 경로가 올바른지 확인합니다.
    2. 이미지 형식이 지원되는지 확인합니다.
    3. 원격 이미지 도메인이 허용 목록에 있는지 확인합니다.
    4. 품질 매개변수가 적절한지 확인합니다.
    5. Sharp가 올바르게 설치됐는지 확인합니다.

    자주 발생하는 문제:
    • Sharp 설치 실패 → Node 버전이 올바른지 확인하고 npm install sharp를 실행합니다.
    • 원격 이미지 최적화 실패 → 도메인 허용 목록 설정을 확인합니다.
    • SSR 모드 미지원 → output이 'server' 또는 'hybrid'로 설정됐는지 확인합니다.

    모범 사례:
    • 이미지 최적화는 지속적인 과정이므로 한 번에 끝낼 필요 없이 단계별로 진행하면 됩니다.
    • 조금씩 최적화할 때마다 성능과 사용자 경험이 좋아집니다.

    지금 바로 할 일:
    1. 사이트를 열고 F12를 눌러 Lighthouse 테스트를 실행해 현재 성능 점수를 확인합니다.
    2. 이미지 형식을 확인하고 WebP로 바꿀 수 있는 이미지는 모두 변환합니다.
    3. 초기 화면 밖의 이미지에 loading="lazy"를 추가합니다.
    4. 이미지가 많다면 Cloudflare CDN 연동을 검토합니다.

FAQ

이미지 최적화가 왜 그렇게 중요한가요?
이미지 최적화가 중요한 이유:
• 이미지는 보통 전체 웹페이지 용량의 60~70%를 차지하며 사이트 성능을 떨어뜨리는 가장 큰 원인입니다.
• 예전에 운영하던 블로그에서는 압축하지 않은 표지 이미지 한 장만 2.5MB였고, 스크린샷 몇 장을 더 넣으면 전체 페이지가 쉽게 5~6MB가 됐습니다.
• 사용자는 사이트를 연 뒤 이미지만 몇 초씩 기다려야 했고 이탈률도 매우 높았습니다.

Google의 이미지 로딩 기준:
• Google의 Core Web Vitals에는 LCP(Largest Contentful Paint, 최대 콘텐츠 렌더링 시간)라는 지표가 있습니다.
• 쉽게 말하면 페이지의 주요 콘텐츠가 로딩을 마치는 데 걸리는 시간입니다.
• Google은 LCP가 2.5초 이내여야 한다고 보며, 4초를 넘으면 낮은 평가를 받습니다.
• 대부분의 사이트에서 LCP 요소는 큰 표지 이미지나 초기 화면 이미지입니다.
• 이미지 로딩이 느리면 LCP가 높아지고 SEO 순위에도 영향을 줍니다.
Astro 이미지 최적화 효과는 어느 정도인가요?
최적화 효과:
• 초기 화면 로딩 시간이 6.2초에서 1.8초로 단축됐습니다(71% 개선).
• Lighthouse 성능 점수가 62점에서 95점으로 올랐습니다(53% 개선).
• 전체 이미지 용량이 약 8MB에서 약 1.2MB로 줄었습니다(85% 감소).
• LCP가 4.8초에서 1.2초로 단축됐습니다(75% 개선).

제 블로그는 최적화 후 초기 화면 로딩이 6.2초에서 1.8초로 줄고 Lighthouse 점수가 62점에서 95점으로 올랐으며 이탈률도 35% 낮아졌습니다. 투자 대비 효과가 매우 컸습니다.
Astro Image 컴포넌트는 어떻게 사용하나요?
Astro Image 컴포넌트의 장점:
• 이미지 형식, 크기, 지연 로딩을 자동으로 최적화합니다.
• <img> 태그 대신 <Image /> 컴포넌트를 사용합니다.
• 압축, 형식 변환, 반응형 처리를 자동으로 수행합니다.

설정 단계:
1) @astrojs/image 통합 패키지를 설치합니다(npx astro add image 실행).
2) astro.config.mjs에서 imageService를 설정합니다(sharp 또는 squoosh 선택).
3) Image 컴포넌트를 사용합니다: import { Image } from 'astro:assets'; <Image src={image} alt="설명" />

로컬 이미지 경로 문제:
• SSR 모드에서는 로컬 이미지를 public/이 아니라 src/ 디렉터리에 넣어야 합니다.
• 올바른 방법: import image from '../assets/photo.jpg'; <Image src={image} alt="올바른 방법" />
• 잘못된 방법: public/의 이미지는 최적화되지 않습니다.
JPEG, PNG, WebP, AVIF는 어떻게 선택하며 어떤 차이가 있나요?
형식 선택:
• JPEG: 사진과 복잡한 이미지에 적합하며 호환성이 가장 좋습니다.
• PNG: 아이콘과 투명 배경에 적합하지만 파일이 큽니다.
• WebP: JPEG보다 30~50% 작고 최신 브라우저가 모두 지원하므로 90%의 상황에서는 WebP면 충분합니다.
• AVIF: WebP보다 20~30% 작지만 호환성이 조금 낮습니다. 극한의 최적화가 필요할 때 대체 형식과 함께 사용합니다.

권장 사항:
• 90%의 상황에서는 WebP를 사용합니다.
• 극한의 최적화가 필요하면 AVIF와 대체 형식을 함께 사용합니다.
• 투명도가 필요하면 WebP 또는 PNG를 사용합니다.
지연 로딩과 CDN 가속은 어떻게 설정하나요?
지연 로딩 설정:
• 초기 화면의 이미지 1~2장은 즉시 로딩하고 나머지는 모두 지연 로딩합니다.
• 초기 로딩 용량을 50% 이상 줄일 수 있습니다.
• loading="lazy" 속성이나 Image 컴포넌트의 loading 속성을 사용합니다.

Cloudflare CDN 연동:
• Cloudflare Images 또는 R2 스토리지를 사용합니다.
• 자동 최적화와 형식 변환을 설정합니다.
• 전 세계 300개 이상의 노드에서 무료로 가속합니다.
• 실제 테스트에서 지연 시간이 3분의 1로 줄었습니다.

설정 단계:
1) Cloudflare Dashboard에서 Images 또는 R2를 활성화합니다.
2) Cloudflare를 사용하도록 imageService를 설정합니다.
3) 이미지를 Cloudflare에 업로드합니다.
4) Cloudflare URL로 이미지를 불러옵니다.
이미지 최적화 문제는 어떻게 진단하나요?
문제 해결 체크리스트:
1) import 경로가 올바른지 확인합니다.
2) 이미지 형식이 지원되는지 확인합니다.
3) 원격 이미지 도메인이 허용 목록에 있는지 확인합니다.
4) 품질 매개변수가 적절한지 확인합니다.
5) Sharp가 올바르게 설치됐는지 확인합니다.

자주 발생하는 문제:
• Sharp 설치 실패 → Node 버전이 올바른지 확인하고 npm install sharp를 실행합니다.
• 원격 이미지 최적화 실패 → 도메인 허용 목록 설정을 확인합니다.
• SSR 모드 미지원 → output이 'server' 또는 'hybrid'로 설정됐는지 확인합니다.

체크리스트 순서대로 확인하면 90%의 문제는 경로, 설정, Sharp 설치에서 원인을 찾을 수 있습니다.

5분 읽기 · 게시일: 2025년 12월 3일 · 수정일: 2026년 9월 8일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog