[NextJS] Image 컴포넌트

✨ 강은비·2022년 6월 19일
9

NextJS

목록 보기
8/10
post-thumbnail

NextJS에서 Image 컴포넌트를 제공하는 것을 모르고 img 태그를 썼는데 아래와 같이 배포 과정에서 warning 메시지가 떴다. 그래서 img 태그를 Image 컴포넌트로 바꾸었고 그 과정에서 공부한 내용들을 정리해보려고 한다.

👀 NextJS 공식문서


왜 Image 컴포넌트를 사용해야 할까? 🤔

Image 컴포넌트는 img 태그의 확장이다. 공식문서에 따르면, 우수한 Core Web Vitals를 달성하기 위해 Image 컴포넌트에 기본으로 최적화 기능이 포함되어 있다. 이때 Core Web Vitals사용자 경험을 측정하는 중요한 척도이고, Google 검색 순위에 반영이 된다.


Image 컴포넌트의 장점 👍

  • Improved Performance : 최신 이미지 형식을 사용하여 언제나 디바이스 사이즈에 맞게 최적화된 이미지를 제공한다.
  • Visual Stability : Cumulative Layout Shift (CLS)를 자동으로 방지해준다.
  • Faster Page Loads : 이미지가 뷰포트에 들어왔을 때만 로드되기 때문에 초기 페이지 로드 속도가 빠르다.
  • Asset Flexibility : 외부에 저장되어 있는 이미지까지도 리사이징이 가능하다.

Image 컴포넌트를 사용하면 img 태그를 사용했을 때보다 사용자 경험이 향상이 되고 기본으로 최적화 기능을 제공하니 img 태그 대신 Image 컴포넌트를 사용하기로 결정했다.



🖼️ Image 컴포넌트 사용하기

Required Props

src
1. 정적으로 불러온 이미지 파일
2. 외부 이미지 경로

이때 외부 이미지 경로를 사용할 때는 next.config.js에서 domains 설정을 해야 한다.

const module.exports = {
	images: {
    	domains: ["image.tmdb.org"],
    },
};

width & height

  • 정적으로 불러오는 이미지 파일과 layout props 값이 fill인 이미지를 제외하고 width랑 height 값이 필수적으로 들어가야 한다.
  • width와 height를 필수적으로 입력해야 하는 이유는 CLS를 방지하기 위해서이다.

Optional Props

layout

  • 뷰포트 변경 시 이미지 레이아웃 처리 방식을 결정한다.
  • fixed : 지정된 width와 height 값을 유지한다.
  • fill : 이미지가 부모 요소 크기만큼 늘어난다. 이때 부모 요소의 position 값이 relative이여야 한다.
  • 👉 layout props 값 자세히 알아보기

placeholder ❣️

  • 이미지가 로드되는 동안 표시될 대체 이미지 사용 여부
  • 사용자들에게 이미지가 로드되고 있다는 것을 명확히 전달할 수 있다.
  • empty (기본) : 대체 이미지가 없다.
  • blur : blurDataURL props 값이 대체 이미지로 사용된다.
    • src가 정적으로 불러온 이미지이고 이 이미지가 .jpg, .png, .webp, .avif라면 blurDataURL이 자동으로 채워진다. (따로 blurDataURL props 값을 전달할 필요 없다.)
    • src가 동적으로 불러오는 이미지라면 blurDataURL props 값을 반드시 전달해야 한다.

blurDataURL

  • 이미지가 로드되는 동안 표시될 대체 이미지 URL
  • placeholder="blur"를 사용할 때 함께 이 props를 사용한다.
  • base64로 인코딩된 이미지여야 하고 10px 이하의 이미지를 권장한다. 더 큰 이미지를 사용하면 애플리케이션 성능이 저하될 수 있다.
  • generate a solid color blur data URL

objectFit

  • layout="fill"를 사용할 때 이미지가 부모 요소에 어떻게 채워질 것인지 정의한다.
  • object-fit CSS 속성값을 전달한다.

👉 더 많은 optional props 알아보기



❣️ Image 컴포넌트 height: auto로 사용하기

Image 컴포넌트를 사용하면서 가장 어려웠던 점은 widthheight를 설정하는 것이었다.
width에 맞는 비율로 height를 auto 값으로 줘야 하는데 Image 컴포넌트는 두 값 모두 필수이다. 😭

방법을 찾다가 아래 글을 읽었다.

NextJS Image 태그 height auto로 사용하기

import Image, { ImageProps } from "next/image";

const AutoHeightImageWrapper = styled.div`
	width: 100%;
	& > span {
		position: unset !important;
		img {
			height: auto !important;
			position: relative !important;
	    }
	}
`;

const AutoHeightImage = ({ ...props } : ImageProps) => {
	<AutoHeightImageWrapper>
	   	<Image layout="fill" {...props} />
   </AutoHeightImageWrapper>
}

export default AutoHeightImage;

위 코드를 응용하여 height 값이 autoPosterImage 컴포넌트를 구현했다!

import Image from "next/image";
import styled from "styled-components";

const Img = styled(Image)`
  height: auto !important;
  position: relative !important;
  box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
  border-radius: 15px;
`;

const PosterImage = ({ src }: { src: string }) => {
  return (
    <Img
      src={`https://image.tmdb.org/t/p/w500/${src}`}
      layout="fill"
      placeholder="blur"
      blurDataURL="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnt2PQAF5AJMrzp1XwAAAABJRU5ErkJggg=="
    />
  );
};

export default PosterImage;

// PosterImage 사용하기 (1)
import PosterImage from "./common/PosterImage";
import { IMovieProps } from "../lib/api/movies";
import styled from "styled-components";

const ImageWrapper = styled.div`
  width: 100%;
  position: relative;
  & > span {
    position: unset !important;
  }
`;

const Movie = ({ movie }: { movie: IMovieProps }) => {
  return (
      <ImageWrapper>
        <PosterImage src={movie.poster_path} />
      </ImageWrapper>
  );
};

export default Movie;

// PosterImage 사용하기 (2)

import PosterImage from "./common/PosterImage";
import { IMovieProps } from "../lib/api/movies";
import styled from "styled-components";

const ImageWrapper = styled.div`
  width: 300px;
  position: relative;
  & > span {
    position: unset !important;
	width: 300px !important;
  }
  @media ${({ theme }) => theme.device.mobile} {
    width: 100%;
    & > span {
        width: 100% !important;
    }
  }
`;

const Movie = ({ movie }: { movie: IMovieProps }) => {
  return (
      <ImageWrapper>
        <PosterImage src={movie.poster_path} />
      </ImageWrapper>
  );
};

export default Movie;

0개의 댓글