현록

next image blurDataURL 직접 부여하기

들어가며

Next.js에서 next/image를 사용하면 이미지 크기 최적화, lazy loading, layout shift 방지 같은 Image Optimization 기능을 활용할 수 있다.

그중 placeholder="blur"는 원본 이미지가 모두 로드되기 전 낮은 품질의 작은 이미지를 먼저 보여주는 옵션이다.
이때 실제로 먼저 보여줄 작은 이미지는 blurDataURL 값으로 전달된다.

다만 모든 상황에서 Next.js가 blurDataURL을 알아서 만들어주지는 않는다.
정적 import인지, 문자열 경로인지, 원격 이미지인지에 따라 처리 방식이 달라진다.

자동으로 처리되는 경우

이미지를 정적으로 import해서 src에 넘기면 Next.js가 이미지 메타데이터를 빌드 시점에 알 수 있다.

import Image from 'next/image'
import thumbnail from '@/public/post/221005_0.png'

export default function PostThumbnail() {
  return <Image src={thumbnail} alt="포스트 썸네일" placeholder="blur" />
}

이 방식은 이미지 파일이 코드에서 직접 import되기 때문에 width, height, blurDataURL 같은 정보를 Next.js가 함께 다룰 수 있다.
블로그 본문에 고정된 이미지를 배치하거나, 컴포넌트가 특정 이미지를 직접 알고 있는 경우라면 이 방식이 가장 단순하다.

직접 blurDataURL이 필요한 경우

문자열로 이미지 경로를 넘기는 경우에는 상황이 다르다.

<Image src="/post/221005_0.png" alt="포스트 썸네일" width={640} height={360} placeholder="blur" />

이 코드는 이미지 주소가 문자열이기 때문에 Next.js가 정적 import처럼 blur placeholder를 자동 생성할 수 없다.
placeholder="blur"를 쓰려면 blurDataURL을 직접 넘겨야 한다.

<Image
  src="/post/221005_0.png"
  alt="포스트 썸네일"
  width={640}
  height={360}
  placeholder="blur"
  blurDataURL="data:image/png;base64,..."
/>

블로그 포스트 목록처럼 frontmatter의 thumb 값을 읽어서 이미지를 렌더링하는 경우가 여기에 해당한다.
원격 이미지 URL을 문자열로 넘기는 경우에도 같은 원칙으로 생각하면 된다.

plaiceholder로 빌드 시점에 만들기

이 블로그에서는 포스트 목록의 썸네일 경로를 마크다운 frontmatter에서 읽는다.
그래서 plaiceholder로 public 이미지 파일을 읽고, 서버에서 포스트 데이터를 만드는 단계에 base64 placeholder를 붙이는 방식을 사용했다.

import fs from 'fs'
import path from 'path'
import { getPlaiceholder } from 'plaiceholder'

const thumbFileBuffer = fs.readFileSync(path.join(process.cwd(), 'public', item.metadata.thumb.replace(/^\//, '')))
const { base64 } = await getPlaiceholder(thumbFileBuffer, { size: 32 })

이렇게 만든 base64 값을 blurDataURL로 넘기면 문자열 경로를 사용하는 이미지에서도 blur placeholder를 사용할 수 있다.

<Image
  src={post.metadata.thumb}
  alt={post.metadata.title}
  width={640}
  height={360}
  placeholder="blur"
  blurDataURL={post.metadata.thumbPoster}
/>

주의할 점

blurDataURL은 작은 이미지라고 해도 결국 문자열 데이터다.
요청이 들어올 때마다 매번 새로 생성하면 CPU와 파일 I/O를 불필요하게 사용하게 된다.

가능하면 빌드 시점에 만들거나, 서버에서 캐싱하거나, CMS에 미리 저장해두는 편이 좋다.
정적 import로 해결되는 이미지라면 직접 생성하지 않고 Next.js의 기본 처리에 맡기는 편이 더 단순하다.

참고 자료

관련 포스트
Next.js App Router에서 검색 날짜와 사이트맵 관리하기 thumbnail
Next.js App Router에서 검색 날짜와 사이트맵 관리하기
Next.js App Router 블로그에서 사용자에게 보이는 날짜, Open Graph modifiedTime, JSON-LD dateModified, sitemap lastModified를 같은 기준으로 연결하는 방법을 정리합니다.
Next.js 16 Cache Components와 use cache 정리 thumbnail
Next.js 16 Cache Components와 use cache 정리
Next.js 16에서 도입된 Cache Components와 use cache directive를 기준으로 App Router 캐싱 모델의 변화, cacheLife, cacheTag, revalidateTag 사용 흐름을 정리합니다.
Next.js App Router에서 레이아웃 사용하기 thumbnail
Next.js App Router에서 레이아웃 사용하기
Next.js App Router에서 app/layout.tsx, page.tsx, 중첩 layout을 사용해 공통 UI를 구성하는 방법을 정리합니다. Pages Router 시절의 수동 Layout 컴포넌트 패턴과 어떤 점이 다른지도 함께 봅니다.
Next.js에서 path alias 설정하기 (feat. @/components) thumbnail
Next.js에서 path alias 설정하기 (feat. @/components)
오늘은 Next.js에서 import 시 복잡한 relative path 대신 absolute path 사용을 위한 설정법을 알아봅시다. file path를 상대경로로 지정하다보면 유지보수면에서도 복잡하고, path를 지정할 때마다 경로가 헷갈려서 발생하는 오류는 덤입니다. 이럴 때 path에 대한 alias를 설정하면 코드는 확 깔끔해질 것입니다.  아래 예제를 통해서 따라해봅시다.
Next.js에서 레이아웃 사용하기 thumbnail
Next.js에서 레이아웃 사용하기
홈페이지를 구성할 때, 우리는 대체로 네비게이션과 푸터, 플로팅 버튼 등을 포함합니다. 개발단계에서 이들 컴포넌트를 페이지마다 일일이 import하는 것은 매우 비효율적인 일입니다. 만약 이들처럼 대부분의 페이지에서 보여줘야될 내용이 있다면, 레이아웃 컴포넌트를 통해 손쉽게 유지보수할 수 있을 것입니다.