Next/Image는 크게 로컬 이미지(정적 이미지)와 리모트 이미지(다이나믹 이미지)로 나뉜다. /public 폴더에 저장한 로컬 이미지는 빌드 타임에 import한 이미지 파일의 width height를 자동으로 지정하고 [base64로 인코딩한 이미지가 생성](https://nextjs.org/docs/api-reference/next/image#:~:text=If src is an object from a static import and the imported image is .jpg%2C .png%2C .webp%2C or .avif%2C then blurDataURL will be automatically populated.)된다. 따라서 추가 작업 없이 블러 처리된 Placeholder를 사용할 수 있다.

<Image
  src="/me.png"
  alt="Picture of the author"
	placeholder="blur"
  // width={500} automatically provided
  // height={500} automatically provided
  // blurDataURL="data:..." automatically provided
/>

그 외 상황은 리모트 이미지로 구분한다. 이때 블러 처리된 Placeholder를 사용하려면 plaiceholder 같은 라이브러리를 사용하거나 캔버스 API를 이용해서 4×4 정도의 사이즈(보통 300바이트 미만)로 줄인 후 base64로 변환하는 작업이 필요하다. NextJS 공식 문서에선 10 픽셀 미만의 사이즈를 권장하고 있다.

방법 1. Canvas 활용


사용자 컴퓨터에서 이미지를 선택한 후 업로드한 경우엔 유틸 함수를 만들어서 사용할 수 있다. 이미지 크롭 등의 상황에서 사용하면 유용하다. ❶로컬 컴퓨터에서 이미지 선택 ➋이미지 크롭 ➌Placeholder로 사용할 base64 문자열을 생성하고, 크롭 이미지 원본은 서버로 전송 ❹크롭 이미지 렌더.



export const toDataURL = (
  img: HTMLImageElement,
  width: number,
  height: number
) => {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

	if (!ctx) throw new Error("No 2d context");

  canvas.width = width;
  canvas.height = height;

  ctx.drawImage(img, 0, 0, width, height);
	// 1번째 인자 : HTMLImageElement, SVGImageElement 등 이미지 소스 엘리먼트
	// 2번째 인자(dx) : 캔버스에 그릴 x축 좌표
	// 3번째 인자(dy) : 캔버스에 그릴 y축 좌표
	// 4번째 인자(dw) : 캔버스에 그릴 width
	// 5번재 인자(dy) : 캔버스에 그릴 height

  return canvas.toDataURL(); // 리사이즈한 이미지를 base64(data URL) 문자열로 변환
};

// 이미지 엘리먼트를 받아 4×4로 리사이즈한 base64 문자열을 반환하는 함수
export const getBlurDataURL = (img: HTMLImageElement) => {
  return toDataURL(img, 4, 4); 
};

<aside> 💡 URL.createObjectURL 대신 FileReader API를 사용할 수도 있다(참고 노트)

</aside>

// <input type="file" ... /> 엘리먼트의 onChange 핸들러
const image = e.target.files?.[0];

if (image) {
  const blobUrl = URL.createObjectURL(image);
  const img = new Image();
	img.src = blobUrl;

  img.onload = () => {
		const base64 = getBlurDataURL(img); // "data:image/png;base64,iVBw...
    URL.revokeObjectURL(blobUrl); // 이미지 로드를 완료하면 메모리 누수 방지를 위해 폐기
		// ...원하는 작업 수행
		// base64 문자열은 Next/Image의 blurDataURL 속성에 사용한다
  };
}

4×4로 리사이즈한 이미지. 평균적으로 200바이트 미만의 용량으로 줄어든다

4×4로 리사이즈한 이미지. 평균적으로 200바이트 미만의 용량으로 줄어든다

방법2. Plaiceholder 라이브러리 활용


<aside> 💡 TailwindCSS 유틸리티 클래스 형태로 사용할 수 있는 @plaiceholder/tailwindcss 플러그인도 있다

</aside>

plaiceholder 라이브러리는 LQIP(저화질 이미지) 생성을 도와주는 NodeJS 라이브러리다. Base64, SVG 등 포맷으로 생성할 수 있다. Next/Image의 blurDataURL 속성에 사용하려면 Base64로 생성하면 된다. 공식 문서에 따르면 Base64 포맷은 일반적으로 ~300 Bytes 미만 사이즈로 생성된다고 한다.

패키지 설치