Next.js 통합#

@snapkit-studio/nextjs 패키지는 Next.js와의 깊이 있는 통합을 제공하며, Next.js Image 컴포넌트 API와 완전히 호환되면서 Snapkit의 강력한 변환 기능을 추가합니다.

설치#

Next.js 프로젝트에 Snapkit을 설치하세요:

npm install @snapkit-studio/nextjs
# 또는
yarn add @snapkit-studio/nextjs
# 또는
pnpm add @snapkit-studio/nextjs

빠른 시작#

환경 변수 설정#

먼저 .env.local에서 환경 변수를 설정하세요:

NEXT_PUBLIC_SNAPKIT_ORGANIZATION_NAME=your-organization-name
NEXT_PUBLIC_SNAPKIT_DEFAULT_QUALITY=85
NEXT_PUBLIC_SNAPKIT_DEFAULT_OPTIMIZE_FORMAT=auto

App Router 설정#

루트 레이아웃에서 Snapkit을 설정하세요:

// app/layout.tsx
import { SnapkitProvider } from '@snapkit-studio/nextjs';
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ko">
      <body>
        <SnapkitProvider
          baseUrl="https://snapkit-cdn.snapkit.studio"
          organizationName="your-org"
          defaultQuality={85}
          defaultFormat="auto"
        >
          {children}
        </SnapkitProvider>
      </body>
    </html>
  );
}

Pages Router 설정#

Pages Router의 경우, _app.tsx에 provider를 추가하세요:

// pages/_app.tsx
import { SnapkitProvider } from '@snapkit-studio/nextjs';
import type { AppProps } from 'next/app';
 
export default function App({ Component, pageProps }: AppProps) {
  return (
    <SnapkitProvider
      baseUrl="https://snapkit-cdn.snapkit.studio"
      organizationName="your-org"
      defaultQuality={85}
      defaultFormat="auto"
    >
      <Component {...pageProps} />
    </SnapkitProvider>
  );
}

주요 기능#

드롭인 교체#

설정 변경 없이 Next.js Image 컴포넌트를 교체하세요:

// 이전
import Image from 'next/image';
 
// 이후
import { Image } from '@snapkit-studio/nextjs';
 
// 사용법은 동일하게 유지
<Image
  src="/hero.jpg"
  alt="히어로 이미지"
  width={800}
  height={600}
  priority
/>

향상된 변환#

Next.js 기능을 넘어서는 강력한 변환 기능을 추가하세요:

import { Image } from '@snapkit-studio/nextjs';
 
<Image
  src="/product.jpg"
  alt="제품 이미지"
  width={400}
  height={300}
  transforms={{
    format: 'auto',
    quality: 90,
    sharpen: true,
    blur: 0,
    grayscale: false
  }}
  className="rounded-lg shadow-md"
/>

자동 최적화#

포맷 선택#

브라우저 지원에 따라 자동으로 최신 포맷을 제공하세요:

<Image
  src="/photo.jpg"
  width={600}
  height={400}
  transforms={{
    format: 'auto' // WebP, AVIF 또는 원본 포맷 제공
  }}
/>

품질 조정#

네트워크 인식 품질 최적화:

<Image
  src="/high-res.jpg"
  width={1200}
  height={800}
  transforms={{
    quality: 'auto' // 연결 속도에 따라 조정
  }}
/>

Next.js 전용 기능#

이미지 최적화#

정적 Import#

빌드 타임 최적화와 함께 정적 이미지 import를 완전히 지원:

import heroImage from '@/public/hero.jpg';
 
<Image
  src={heroImage}
  alt="히어로"
  placeholder="blur"
  priority
  transforms={{ format: 'auto' }}
/>

동적 Import#

동적으로 로드되는 이미지의 경우:

<Image
  src={`/products/${productId}.jpg`}
  width={500}
  height={500}
  transforms={{
    format: 'auto',
    fit: 'cover'
  }}
/>

반응형 이미지#

컨테이너 채우기#

반응형 컨테이너 기반 레이아웃의 경우:

<div className="relative aspect-video">
  <Image
    src="/banner.jpg"
    alt="배너"
    fill
    sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
    className="object-cover"
    transforms={{ format: 'auto' }}
  />
</div>

아트 디렉션#

다양한 브레이크포인트를 위한 다른 이미지:

<picture>
  <source
    media="(max-width: 768px)"
    srcSet="/mobile-hero.jpg"
  />
  <Image
    src="/desktop-hero.jpg"
    width={1920}
    height={1080}
    alt="반응형 히어로"
    transforms={{ format: 'auto' }}
  />
</picture>

성능 기능#

우선순위 로딩#

LCP 최적화를 위해:

<Image
  src="/above-fold.jpg"
  width={1200}
  height={600}
  priority
  transforms={{
    format: 'auto',
    quality: 90
  }}
/>

플레이스홀더와 함께 지연 로딩#

부드러운 로딩 경험:

<Image
  src="/gallery-image.jpg"
  width={400}
  height={300}
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
  loading="lazy"
/>

설정#

next.config.js 설정#

Snapkit을 이미지 최적화 솔루션으로 설정하세요:

// next.config.js
module.exports = {
  images: {
    domains: ['snapkit-cdn.snapkit.studio'],
    loader: 'custom',
    loaderFile: './snapkit-loader.js',
  },
};

커스텀 로더#

고급 사용 사례를 위한 커스텀 로더 생성:

// snapkit-loader.js
export default function snapkitLoader({ src, width, quality }) {
  const url = new URL(`https://your-org-cdn.snapkit.studio/${src}`);
 
  const params = {
    w: width,
    q: quality || 75,
    auto: 'format',
  };
 
  Object.keys(params).forEach(key =>
    url.searchParams.set(key, params[key])
  );
 
  return url.href;
}

App Router 기능#

서버 컴포넌트#

React Server Components를 완전히 지원:

// app/gallery/page.tsx
import { Image } from '@snapkit-studio/nextjs';
 
export default async function Gallery() {
  const images = await fetchImages();
 
  return (
    <div className="grid grid-cols-3 gap-4">
      {images.map((img) => (
        <Image
          key={img.id}
          src={img.url}
          width={400}
          height={300}
          alt={img.alt}
          transforms={{ format: 'auto' }}
        />
      ))}
    </div>
  );
}

클라이언트 컴포넌트#

클라이언트 사이드 기능과의 원활한 통합:

'use client';
 
import { useState } from 'react';
import { Image } from '@snapkit-studio/nextjs';
 
export default function InteractiveGallery() {
  const [selectedImage, setSelectedImage] = useState(0);
 
  return (
    <Image
      src={images[selectedImage]}
      width={800}
      height={600}
      transforms={{
        format: 'auto',
        quality: 90
      }}
    />
  );
}

고급 사용법#

메타데이터 생성#

SEO와 소셜 공유를 위해:

// app/products/[id]/page.tsx
import { Metadata } from 'next';
 
export async function generateMetadata({ params }): Promise<Metadata> {
  const product = await getProduct(params.id);
 
  return {
    openGraph: {
      images: [{
        url: `https://your-cdn.snapkit.studio/${product.image}?w=1200&h=630&fit=cover`,
        width: 1200,
        height: 630,
      }],
    },
  };
}

ISR과 SSG#

Incremental Static Regeneration을 완전히 지원:

// app/blog/[slug]/page.tsx
export const revalidate = 3600; // 매 시간마다 재검증
 
export default async function BlogPost({ params }) {
  const post = await getPost(params.slug);
 
  return (
    <Image
      src={post.featuredImage}
      width={1200}
      height={600}
      alt={post.title}
      priority
      transforms={{ format: 'auto' }}
    />
  );
}

Edge Runtime#

Edge runtime에 최적화:

export const runtime = 'edge';
 
export default function EdgeOptimizedPage() {
  return (
    <Image
      src="/edge-optimized.jpg"
      width={600}
      height={400}
      transforms={{
        format: 'auto',
        quality: 85
      }}
    />
  );
}

마이그레이션 가이드#

next/image에서#

최소한의 변경이 필요합니다:

// 1단계: import 업데이트
- import Image from 'next/image';
+ import { Image } from '@snapkit-studio/nextjs';
 
// 2단계: transforms 추가 (선택사항)
<Image
  src="/photo.jpg"
  width={500}
  height={300}
+ transforms={{ format: 'auto' }}
/>

레거시 Next.js에서#

Next.js 12 이하 버전의 경우:

// 이전 (Next.js 12)
<Image
  src="/photo.jpg"
  width={500}
  height={300}
  layout="responsive"
/>
 
// 이후 (Snapkit 사용)
<Image
  src="/photo.jpg"
  width={500}
  height={300}
  sizes="100vw"
  style={{
    width: '100%',
    height: 'auto',
  }}
  transforms={{ format: 'auto' }}
/>

성능 최적화#

빌드 타임 최적화#

빌드 중에 최적화된 이미지 생성:

// next.config.js
module.exports = {
  experimental: {
    optimizeImages: true,
  },
};

런타임 최적화#

사용자 컨텍스트에 기반한 동적 최적화:

<Image
  src="/dynamic.jpg"
  width={800}
  height={600}
  transforms={{
    format: 'auto',
    quality: typeof window !== 'undefined'
      ? navigator.connection?.saveData ? 60 : 85
      : 85,
  }}
/>

모범 사례#

  1. App Router 사용: 더 나은 성능을 위해 React Server Components를 활용하세요.

  2. 캐싱 활성화: 최적화된 이미지를 위한 적절한 캐시 헤더를 설정하세요.

  3. Above-the-fold 최적화: 중요한 이미지에는 priority prop을 사용하세요.

  4. 반응형 이미지: 반응형 레이아웃에는 항상 sizes prop을 제공하세요.

  5. 자동 포맷: Snapkit이 자동으로 최적의 포맷을 선택하도록 하세요.

문제 해결#

일반적인 문제들#

  1. Hydration 불일치: 서버와 클라이언트 간 일관된 렌더링을 확인하세요.

  2. CORS 문제: next.config.js에서 domains을 설정하세요.

  3. 빌드 오류: 모든 정적 import가 유효한지 확인하세요.

  4. 성능 문제: 캐싱 설정을 확인하세요.

주요 기능 요약#

  • 원활한 Next.js Image 컴포넌트 통합
  • 자동 이미지 변환
  • 동적 DPR 기반 srcset 생성
  • 지능적 포맷 선택 (WebP, AVIF)
  • 서버 및 클라이언트 컴포넌트 지원
  • TypeScript 정의
  • 90%+ 테스트 커버리지

리소스#