반응형 이미지 아트 디렉션: picture, srcset으로 완벽한 크로스 디바이스

디자인

반응형 이미지아트 디렉션웹 성능이미지 최적화LCP

이 글은 누구를 위한 것인가

  • 모바일에서는 크롭된 이미지, 데스크탑에서는 원본 이미지를 제공하려는 팀
  • WebP/AVIF를 지원 브라우저에만 제공하고 싶은 개발자
  • LCP 점수 개선을 위해 이미지를 최적화하려는 팀

들어가며

<img src="hero.jpg">만으로는 4K 이미지를 모바일에서도 다운로드한다. srcset으로 디바이스 픽셀 밀도와 뷰포트 크기에 맞는 이미지를 제공하고, picture로 모바일에서는 세로 구도, 데스크탑에서는 가로 구도를 아트 디렉션한다.

이 글은 bluefoxdev.kr의 반응형 이미지 아트 디렉션 가이드 를 참고하여 작성했습니다.


1. 반응형 이미지 전략

[srcset vs picture]
  srcset + sizes: 같은 이미지, 다른 크기
    → 브라우저가 최적 크기 자동 선택
    
  picture: 다른 이미지 (아트 디렉션)
    → 모바일: 얼굴 클로즈업 / 데스크탑: 전체 구도
    → WebP 지원: WebP / 미지원: JPEG

[sizes 속성]
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 50vw,
         33vw"
  
  브라우저는 sizes로 표시 크기를 알고
  srcset에서 가장 적합한 이미지 선택

[레이아웃 시프트 방지]
  width + height 속성 필수
  또는 aspect-ratio CSS
  → 이미지 로드 전 공간 예약

[LCP 최적화]
  히어로 이미지: fetchpriority="high"
  Above-the-fold: loading="eager"
  Below-the-fold: loading="lazy"
  preload link: <link rel="preload" as="image">

2. 반응형 이미지 구현

<!-- 기본 srcset (해상도 디스크립터) -->
<img
  src="hero-800.jpg"
  srcset="hero-400.jpg 400w,
          hero-800.jpg 800w,
          hero-1200.jpg 1200w,
          hero-2400.jpg 2400w"
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 80vw,
         1200px"
  alt="서비스 히어로 이미지"
  width="1200"
  height="600"
  fetchpriority="high"
/>

<!-- picture: 아트 디렉션 + 포맷 분기 -->
<picture>
  <!-- 모바일: 세로 구도, AVIF -->
  <source
    media="(max-width: 640px)"
    type="image/avif"
    srcset="hero-mobile.avif"
  />
  <!-- 모바일: 세로 구도, WebP 폴백 -->
  <source
    media="(max-width: 640px)"
    type="image/webp"
    srcset="hero-mobile.webp"
  />
  <!-- 데스크탑: 가로 구도, AVIF -->
  <source
    media="(min-width: 641px)"
    type="image/avif"
    srcset="hero-desktop-800.avif 800w, hero-desktop-1600.avif 1600w"
    sizes="(max-width: 1024px) 80vw, 1200px"
  />
  <!-- 데스크탑: 가로 구도, WebP 폴백 -->
  <source
    media="(min-width: 641px)"
    type="image/webp"
    srcset="hero-desktop-800.webp 800w, hero-desktop-1600.webp 1600w"
    sizes="(max-width: 1024px) 80vw, 1200px"
  />
  <!-- 최종 폴백 -->
  <img
    src="hero-desktop.jpg"
    alt="서비스 히어로"
    width="1600"
    height="800"
    fetchpriority="high"
  />
</picture>
/* 레이아웃 시프트 방지 */
.hero-image {
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;
  object-position: center;
}

/* 모바일 아트 디렉션 */
@media (max-width: 640px) {
  .hero-image {
    aspect-ratio: 4 / 5; /* 세로 구도 */
    object-position: top center; /* 얼굴 중심 */
  }
}

/* 이미지 갤러리 그리드 */
.image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}

.image-grid img {
  aspect-ratio: 4 / 3;
  width: 100%;
  object-fit: cover;
  border-radius: 8px;
}
// Next.js Image 컴포넌트 (자동 최적화)
import Image from 'next/image';

function HeroSection() {
  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
      <Image
        src="/images/hero.jpg"
        alt="서비스 소개"
        fill
        sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 1200px"
        priority // LCP 이미지: 지연 로딩 비활성화
        style={{ objectFit: 'cover' }}
      />
    </div>
  );
}

마무리

반응형 이미지의 핵심은 srcset + sizes로 브라우저가 최적 이미지를 선택하게 하고, picture로 아트 디렉션과 포맷 분기를 처리하는 것이다. LCP 이미지에는 fetchpriority="high"<link rel="preload">를 적용하고, width + height 또는 aspect-ratio로 레이아웃 시프트(CLS)를 방지한다.