본문 바로가기
Frontend2025년 9월 20일6분 읽기

Next.js Image 최적화 심화 — blur placeholder와 CDN 전략

YS
김영삼
조회 520

Next.js Image 컴포넌트 심화

Next.js의 Image 컴포넌트는 자동 리사이징, WebP/AVIF 변환, 지연 로딩을 기본 제공합니다. 여기서 더 나아가 blur placeholder와 CDN 연동을 통해 사용자 경험을 극대화할 수 있습니다.

정적 이미지 blur placeholder

// 정적 import — 자동으로 blur placeholder 생성
import heroImage from '@/public/images/hero.jpg';

export default function Hero() {
  return (
    <Image
      src={heroImage}
      alt="히어로 이미지"
      placeholder="blur"
      priority
      sizes="100vw"
      quality={85}
    />
  );
}

동적 이미지 blur placeholder 생성

// lib/image-utils.ts
import { getPlaiceholder } from 'plaiceholder';

export async function getBlurDataUrl(src: string) {
  try {
    const res = await fetch(src);
    const buffer = Buffer.from(await res.arrayBuffer());
    const { base64 } = await getPlaiceholder(buffer, { size: 10 });
    return base64;
  } catch (e) {
    return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
  }
}

export default async function PostImage({ src, alt }) {
  const blurDataUrl = await getBlurDataUrl(src);
  return (
    <Image
      src={src}
      alt={alt}
      width={800}
      height={450}
      placeholder="blur"
      blurDataURL={blurDataUrl}
    />
  );
}

외부 CDN 설정 (next.config.js)

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: '**.cloudinary.com' },
      { protocol: 'https', hostname: 'images.unsplash.com' },
    ],
    loader: 'custom',
    loaderFile: './lib/cloudinary-loader.ts',
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    formats: ['image/avif', 'image/webp'],
  },
};

module.exports = nextConfig;

Cloudinary 커스텀 로더

// lib/cloudinary-loader.ts
export default function cloudinaryLoader({ src, width, quality }) {
  const params = [
    'f_auto', 'c_limit',
    'w_' + width,
    'q_' + (quality || 'auto'),
  ];
  return 'https://res.cloudinary.com/mycloud/image/upload/' + params.join(',') + '/' + src;
}

반응형 이미지 sizes 속성

<Image
  src="/photos/landscape.jpg"
  alt="풍경"
  fill
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
  style={{ objectFit: 'cover' }}
/>

{images.map((img) => (
  <div key={img.id} className="relative aspect-video">
    <Image
      src={img.url}
      alt={img.alt}
      fill
      sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
      placeholder="blur"
      blurDataURL={img.blurUrl}
    />
  </div>
))}
  • LCP(Largest Contentful Paint) 이미지에는 반드시 priority를 설정합니다
  • sizes 속성을 정확히 지정해야 불필요한 큰 이미지 다운로드를 방지합니다
  • AVIF는 WebP 대비 20-30% 더 작지만, 인코딩 시간이 깁니다
  • blur placeholder는 CLS(Cumulative Layout Shift) 방지에도 효과적입니다

이미지 최적화는 웹 성능의 가장 큰 개선 포인트입니다. Next.js Image 컴포넌트와 CDN을 적절히 조합하면 최소한의 코드로 최대의 성능 개선을 달성할 수 있습니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록