Front-End(Web)/React - 프레임워크(React, Next)

[Next.js] next/image - <Image> 컴포넌트

ttaeng_99 2022. 3. 13. 13:22
반응형

이번에 토이를 Next.js로 진행하면서, 이미지를 Next.js에서 지원하는 <Image> 컴포넌트로 적용하는 것을 권장한다는 메세지를 보았다.

Next.js v10부터 <Image> 컴포넌트를 지원했으며, 주요 목적이 바로 이미지 최적화(Optimization) 인 것이다.

 

<Image> 컴포넌트를 사용하는 의의와 사용법에 대해 간단하게 공부하면서 정리해보았다!

 


 

🤍 <Image> 컴포넌트는?

<Image> 는 HTML의 <img> 태그에서 확장되어 built-in을 최적화하는 Next.js의 이미지 컴포넌트이다. 

Next.js 공식문서에서 설명하는 <Image> 컴포넌트 사용의 의의는 아래와 같다.

 

Basic Features: Image Optimization | Next.js

Next.js supports built-in image optimization, as well as third party loaders for Imgix, Cloudinary, and more! Learn more here.

nextjs.org

  • Improved Performance : modern image format을 사용해서 각각의 디바이스에 맞는 적절한 이미지 사이즈를 제공
  • Visual Stability : Cumulative Layout Shift(누적 레이아웃 이동) 이 발생하지 않도록 안정성을 보장
  • Faster Page Loads : 뷰포트에 들어갈때만 이미지가 로드된다.
  • Asset Flexibility : remote 서버(클라우드, CMS 등)에 저장된 이미지도 리사이징이 가능

 

 

* <img> 태그 사용했을 때 문제점

 

이 링크에서 <img> 태그를 사용했을 때 문제점, 그래서 Next.js <Image> 컴포넌트를 사용해야 하는 이유를 잘 설명한다.

주요 이슈 중 하나는, 위처럼 렌더링 사이즈가 360 x 240 이지만, 이미지 크기 자체는 2400 x 1598 (344kb)로 상당히 큰 것을 알 수 있다. 이러한 비효율적인 이미지 렌더링을 포함한, 다양한 문제들이 존재할 수 있다.

  • 리사이징 문제 : 서버에 디바이스 별 크기에 맞는 이미지를 저장하고, 디바이스 너비에 따라 호출해야 하는데 이는 코드가 방대해진다.
  • 모던 이미지 포맷 문제 : WebP(구글의 이미지 포맷)과 같은 현대 이미지 포맷의 적용과, 이를 지원하지 않는 브라우저는 기존 포맷(JPEG, PNG) 등으로 대체해야 한다는 리소스가 존재한다.
  • 레이지 로딩 문제 : 용량이 큰 이미지의 로드가 지연될 때 placeholder가 필요하며, react-lazyload 등의 서드파티가 요구된다.

 

위같은 문제점들을 보완하고, 이미지가 필요한 경우에먼 최적화를 하여 Build 및 Rendering 효율을 향상시키기 위해 <Image> 컴포넌트가 등장하게 된 것이다!

 

 

 

🤍 <Image> 컴포넌트 사용법

 

- 적용

import Image from 'next/image'

이처럼, <Image> 컴포넌트가 필요한 곳에서 이를 'next/image' 로부터 import 해서 사용하면 된다.

 

// Local Image

import Image from 'next/image'
import profilePic from '../public/me.png'

function Home() {
  return (
    <>
      <Image
        src={profilePic}
        alt="Picture of the author"
        // width={500} automatically provided
        // height={500} automatically provided
        // blurDataURL="data:..." automatically provided
        // placeholder="blur" // Optional blur-up while loading
      />
    </>
  )
}
// Remote Image

import Image from 'next/image'

export default function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src="/me.png"
        alt="Picture of the author"
        width={500}
        height={500}
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

위처럼, 프로젝트 폴더 내 Local Image, 그리고 저장소의 Remote Image 두 케이스 모두에 적용할 수 있다.

 

Local Image는 자동으로 width, height를 계산해주지만(Cumulative Layout Shift 미발생),

Remote Image는 Next.js가 빌드 시점에 파일에 접근할 수 없으므로 width, height props를 필수로 입력해줘야 한다.

 

 

- 설정(Configuration)

next.config.js 파일 images 프로퍼티 내에서 Image Optimization 관련된 설정들을 적용할 수 있다. (공식문서 링크)

// next.config.js

module.exports = {
  images: {
    domains: ['cloud.com'],
    loader: 'imgix',
    path: 'https://example.com/myaccount/',
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
  // ...
}
  • Domains : Remote Images를 가져오는 CMS 등의 URL 주소를 지정한다. 이 링크의 이미지에 대해 최적화를 지원하게된다.
  • Loader : Next.js는 기본적으로 이미지 로더를 제공한다.(Vercel) 외에, imgix, cloudinary, akamai 등 로더를 설정할 수 있다.
  • Caching : default 이미지 로더를 사용할때, 제공된 최적화 이미지가 <distDir>/cache/images 에 캐싱되는 것에 관련된 설정.
  • Device Sizes : 디바이스 사이즈 별 breakpoint 설정이다. layout 프로퍼티가 responsive, fill 일 때 적용된다.
  • Image Sizes : deviceSizes 배열과 연결되어 이미지 srcset을 생성하는 배열. 이 값들은 deviceSizes 최소값보다 작아야 한다.

이외에도 formats(지원 이미지 포맷), minimumCacheTTL(캐싱 이미지 수명), disableStaticImage(정적 이미지 비활성화), dangerouslyAllowSVG(svg 포맷 지원) 등의 옵션을 설정할 수 있다.

 

 

- 속성(Props)

HTML <img> 태그와 유사하게, <Image> 컴포넌트도 요구되거나 추가적인 설정을 위한 속성값들이 존재한다.

import Image from 'next/image'

const exampleLoader = ({ src, width, height }) => {
  return `https://cdn.sanity.io/${src}?w=${width}&q=${quality || 75}`
}

<Image

  // Required Props
  src="https://cdn.com/image.webp"
  alt="next image"
  width={40}
  height={40}
  
  // Optional Props
  layout="responsive"
  loader={exampleLoader}
  sizes="320 640 750"
  quality={100}
  priority
  placeholder="blur"
  blurDataUrl="../public/blur.jpg"
  
  // Advanced Props
  objectFit="contain"
  objectPosition="top right"
  loading="lazy"
  onLoadingComplete={(imageDimension} => console.log(imageDimension)}
  lazyBoundary="200px"
  unoptimized
/>

 

1) src (필수)

 

이미지 출처값으로, 프로젝트 디렉토리 내 정적경로 혹은 외부 도메인의 URL 스트링(config 설정 필요) 을 넘겨줘야 한다.

 

 

2) width & height (필수)

 

이미지의 픽셀 너비 및 높이. 단위를 제외한 정수값을 전달해줘야 한다. (정적 이미지 혹은 layout="fii" 이 아닌 경우 요구된다.)

 


 

3) layout

 

Viewport의 크기가 변경될 때 이미지 레이아웃 동작을 설정한다.

  • intrinsic : 기본값. 이미지 사이즈보다 작은 뷰포트에선 크기가 줄어들지만, 큰 뷰포트에선 본래 크기를 유지.
  • fixed : 정확한 width와 height 값에 맞춤.
  • responsive : 이미지가 작은 뷰포트에선 크기가 줄어들고, 큰 뷰포트에선 맞춰 너비와 높이가 늘어난다. (부모요소가 'display: block' 이어야함)
  • fill : 부모 요소의 크기에 맞춰 너비와 높이가 늘어난다. objectFit 속성과 함께 사용한다. (부모 요소가 'position: relative' 이어야함)

 

4) loader

 

이미지 URL을 반환하는 함수로, 사용자 지정 함수를 사용하거나 기본적으로는 next.config.js의 loader를 오버라이드한다.

loader 함수는 src, width, quality를 파라미터로 받아 이미지 소스의 URL을 반환한다.

 

 

5) sizes

 

breakpoints에서 이미지가 어느 너비를 가질지 설정한다.

layout(responsive, fill) 의 경우 뷰포트보다 작은 이미지는 기본 너비가 100vw이므로 이 크기를 지정하는 것이 중요하다.

layout(intrinsic, fixed)는 너비의 상한선이 제약이 있어 이 속성을 적용하지 않는다.

 

 

6) quality

 

최적화된 이미지 품질로, 1~100 사이의 정수를 부여한다. 기본값은 75.

 

 

7) priority

 

true로 설정된 이미지는 높은 우선순위와 preload를 가진다. 이 이미지들의 lazy loading은 자동으로 비활성화된다.

상단에 보여지는 이미지들에 주로 사용되며, LCP(Largest Contentful Paint) 요소로 감지되는 이미지들은 priority를 사용해야 한다.

 

 

8) placeholder (+ blurDataURL)

 

이미지가 로드동안 사용되는 보여지는 fallback을 설정한다. 'empty' 혹은 'blur' 로 설정할 수 있다.(기본값은 'empty')

blur인 경우, 정적 이미지(jpg, png, webp, avif)는 blurDataURL이 자동으로 채워지지만 동적 이미지의 경우 blurDataURL로 전달한 이미지가 fallback으로 사용된다.

 


 

9) objectFit

 

CSS의 'object-fit' 속성과 유사하다. layout(fill)의 경우 부모요소 내에서 이미지 맞춤 방식을 정하며, 이 값은 CSS로서 하달된다.

(contain, cover, fill, none, scale-down)

 

 

10) objectPosition

 

CSS의 'object-position' 속성과 유사하다. layout(fill)의 경우 부모요소 내에서 이미지 배치 방식을 정하며, 역시 CSS로서 하달된다.

 

 

11) loading

 

<img> 태그의 loading 속성과 유사하다. 'lazy'(레이지 로딩) 혹은 'eager'(즉시) 로 설정할 수 있다.

단, 'eager'는 이미지 퍼포먼스에 악영향을 줄 수 있으므로, priority props로 대체할 것을 권장한다.

 

 

12) onLoadingComplete

 

이미지가 완전히 로드되고 placeholder가 사라지는 시점에 호출되는 콜백 함수이다.

imageDimension 값을 인자로 받으며, 이 안에는 naturalWidth, naturalHeight 값들이 저장되어있다.

 

 

13) lazyBoundary

 

이미지와 뷰포트의 교차를 감지하고 lazy loading을 트리거하는 경계 상자이다. 좌우 마진과 유사하며, 기본값은 200px이다.

 

 

14) unoptimized

 

true인 경우, 원본 이미지의 품질, 크기, 포맷을 변경하지 않고 본래값 그대로 제공한다. (그러면 <Image>를 쓰는 의미가..)

 

 

15) 기타 Props

  • style -> 대신, className으로 스타일링한다.
  • srcSet -> 대신, deviceSizes를 적용한다.
  • ref -> 대신, onLoadingComplete를 적용한다.
  • decoding -> 항상 async로 적용된다.

<Image> 컴포넌트를 권장하고, 여기서 외부 링크를 이용할 때 width, height 값을 부여해야한다는 메세지로 시작된 공부가 그생각보다 짧지는 않게 끝났다..!! 😇😇

 

확실히 Next.js는 프레임워크인 만큼, <Image> 컴포넌트 안에서도 다양한 옵션들을 제공하고 있었다.

이미지 최적화뿐만 아니라, Loader나 Placeholder 등을 설정할 수 있는 부분들도 유용하다고 생각이 되었다.

 

특히, 원론적으로 이미지 최적화가 필요한 이유를 상단의 예시를 통해 공감하고 이를 사용하는 것이 무엇보다 중요하다고 생각된다!

 

 

 

- [next/image 공식문서] Next.js 공식문서 : https://nextjs.org/docs/api-reference/next/image#configuration-options

- [next/image 공식문서 번역] birdmee 님의 블로그 : https://birdmee.tistory.com/38  

- [<Image> props & configuration] logrocket's blog : https://blog.logrocket.com/next-js-automatic-image-optimization-next-image/ 

반응형