1. Image 컴포넌트


Next.js에서는 sharp 라이브러리와 결합해 이미지 최적화 기능을 제공합니다.

<aside>

Next.js 환경에서 Image 컴포넌트와 sharp 라이브러리의 활용은 강력하게 권장됩니다. 이미지가 웹페이지에서 가장 큰 자원을 점유하는 콘텐츠 중 하나인만큼 최적화를 사용하냐 아니냐에 따라 웹에서의 성능이 크게 달라집니다.

2. 커스텀 Image 컴포넌트


Trablock 프로젝트는 이미지를 많이 사용하는 서비스이므로 필연적으로 Image 컴포넌트를 많이 사용합니다. 이 과정에서 공통으로 적용하는 속성이 있었고, 이를 공통화하여 정리하는 과정이 필요했습니다. 필요한 기능을 정리하고 구현한 첫 코드는 다음과 같았습니다.

<aside>

  1. image-cover 속성 적용
  2. priority 에 따라 loading 자동 지정 </aside>
interface NextImageProps extends Omit<ImageProps, 'width' | 'height'> {
  width: number;
  height: number;
}

function NextImage({
  className,
  src,
  alt,
  width,
  height,
  loading = 'lazy',
  priority,
  ...restImageProps
}: NextImageProps) {
  return (
    <div className={`overflow-hidden ${className}`}>
      <Image
        {...restImageProps}
        className="image-cover"
        src={src}
        alt={alt}
        width={width}
        height={height}
        loading={priority ? 'eager' : loading}
        priority={priority}
      />
    </div>
  );
}

위 코드를 사용하다보니 불편한 점이 있었습니다. 만약 src가 빈 문자열일 경우 Image 컴포넌트에서 컴파일 에러가 발생하는데, 이 때 에러를 처리하기 위해서는 src가 없을 때 표시할 대체 이미지를 사용하거나 Image 컴포넌트 자체를 렌더링하지 않아야 합니다. 이 문제를 위해 추가하고자 한 기능은 다음과 같습니다.

<aside>

  1. src가 없을 경우 배경색으로 공간을 채운다.
  2. src가 있을 경우 Image 컴포넌트를 렌더링한다. </aside>
export interface NextImageProps extends Omit<ImageProps, 'width' | 'height' | 'src'> {
  placeholderClassName?: string;
  width: number;
  height: number;
  src?: ImageProps['src'];
}

export default function NextImage({
  className,
  placeholderClassName,
  src,
  alt,
  width,
  height,
  loading = 'lazy',
  priority,
  ...restImageProps
}: NextmageProps) {
	// src가 없을 때 배경색 렌더링
  if (!src) {
    return (
      <div className={`overflow-hidden ${className}`}>
        <div className={`size-full bg-gray-02 ${placeholderClassName}`} />
      </div>
    );
  }

	// src가 있을 때 Image 컴포넌트 렌더링
  return (
    <div className={`overflow-hidden ${className}`}>
      <Image
        {...restImageProps}
        className="image-cover"
        src={src}
        alt={alt}
        width={width}
        height={height}
        loading={priority ? 'eager' : loading}
        priority={priority}
      />
    </div>
  );
}

src가 있을 때 Image 컴포넌트를 렌더링하고, 없을 때 플레이스홀더를 렌더링하게 처리하는 로직에는 처리해야하는 문제가 추가적으로 발생했습니다.

<aside>

  1. src가 유효하지 않거나 서버에서 이미지를 가져오는데 실패할 경우 못생긴 아이콘과 텍스트가 표시되는 문제

  2. 이미지가 로드되기 전에 빈 공간이 유지되는 문제

  3. 원본 이미지의 비율이 다양해 이미지의 크기가 정해진 width, height 중 하나보다 작은 경우가 발생하는 문제

따라서 기존 컴포넌트는 NextServerImage로 분리해 서버 측 전용으로 사용하고, NextImage를 클라이언트 측 전용으로 전환해 문제를 해결할 기능을 추가했습니다.