Next.js 13에서 이미지 로딩 시 Layout Shift 문제 해결하기



Next.js 13에서 이미지 로딩 시 Layout Shift 문제 해결하기

Next.js 13을 사용하여 프로젝트를 진행하던 중, 이미지 로딩 시 layout shift 현상을 경험했습니다. 이 문제는 이미지 컴포넌트를 잘못 설정하여 발생했으며, 이를 해결하기 위한 방법을 정리해보았습니다.

 

👉 ✅ 상세 정보 바로 확인 👈

 

layout shift의 원인

Next.js에서 제공하는 <Image> 컴포넌트를 사용할 때는 반드시 너비와 높이를 지정해야 합니다. 하지만, 동적으로 이미지를 불러오는 경우에는 이 크기를 미리 알 수 없기 때문에, 다음과 같이 코드를 작성하게 됩니다.



jsx
<Image
src={src}
alt={src}
width={0}
height={0}
sizes="65vw"
style={{ height: "auto" }}
/>

이 코드에서는 width와 height 모두 0으로 설정되어 있어, 실제 이미지가 로드될 때 밀림 현상이 발생하는 것이었습니다. 따라서 이러한 설정으로 인해 사용자가 이미지를 로딩하는 동안 빈 화면이 보이는 현상이 발생하게 되었습니다.

 

👉 ✅ 상세 정보 바로 확인 👈

 

이미지 로딩 시 Placeholder 사용하기

Next.js의 <Image> 컴포넌트는 이미지 로딩 전에 보여줄 placeholder 속성을 제공하고 있습니다. 기본값은 “empty”이며, “blur” 옵션을 사용할 경우에는 blurDataURL이 필요합니다. 이 속성은 블러 이미지의 주소로, base64로 인코딩된 최대 10픽셀짜리 이미지여야 합니다.

정적으로 이미지를 불러올 경우 blurDataURL이 자동으로 생성되므로, placeholder="blur"만 설정하면 됩니다. 하지만 동적으로 이미지를 불러오는 경우에는 blurDataURL을 수동으로 작성해야 합니다. 이때 plaiceholder 라이브러리를 사용하는 것이 좋습니다.

Plaiceholder 라이브러리 설치

plaiceholder는 이미지의 placeholder를 생성하는 라이브러리입니다. 다음과 같이 설치할 수 있습니다.

bash
npm install sharp
npm install plaiceholder
npm install @plaiceholder/next

Next.js 설정 파일(next.config.mjs)에서 withPlaiceholder를 사용하여 설정을 추가해야 합니다.

“`javascript
// next.config.mjs
import withPlaiceholder from “@plaiceholder/next”;
import withImages from “next-images”;

const nextConfig = withImages({
experimental: {
appDir: true,
},
images: {
formats: [“image/avif”, “image/webp”],
},
swcMinify: true,
});

export default withPlaiceholder(nextConfig);
“`

이미지 Placeholder 코드 작성하기

다음은 getBase64 함수를 통해 이미지를 읽어와 placeholder를 설정하는 방법입니다.

“`javascript
// utils/getBase64.ts
import fs from “node:fs/promises”;
import path from “node:path”;
import { getPlaiceholder } from “plaiceholder”;

const getBase64 = async (src: string) => {
const buffer = await fs.readFile(path.join(“./public”, src));

const {
metadata: { height, width },
…plaiceholder
} = await getPlaiceholder(buffer, { size: 10 });

return {
…plaiceholder,
img: { src, height, width },
};
};

export default getBase64;
“`

이 함수를 사용하여 이미지의 buffer를 생성하고, placeholder와 이미지의 크기를 반환받을 수 있습니다.

ImgWithPlaceholder 컴포넌트 구현

아래는 placeholder를 적용한 이미지 컴포넌트입니다.

“`javascript
// components/ImgWithPlaceholder.tsx
import getBase64 from “@/utils/getBase64”;
import Image from “next/image”;

async function ImgWithPlaceholder({ src }: { src: string }) {
const { base64, img } = await getBase64(src);

return (
{src}
);
}

export default ImgWithPlaceholder;
“`

이제 이 컴포넌트를 다음과 같이 사용할 수 있습니다.

jsx
<ImgWithPlaceholder src={`/media/${data.message}`} />

결과 및 주의사항

테스트 후, 더 이상 layout shift가 발생하지 않고, 이미지가 로딩되기 전 블러 이미지가 잘 보이는 것을 확인했습니다. Lighthouse 결과에서도 Cumulative Layout Shift가 사라진 것을 확인할 수 있었습니다.

하지만 plaiceholder는 서버 측 라이브러리이므로, 클라이언트 측에서 직접 사용할 수 없습니다. 서버 컴포넌트에서만 사용할 수 있다는 점에 유의해야 합니다.

또한, 프로젝트의 상위 컴포넌트들이 클라이언트 컴포넌트일 경우, UnhandledSchemeError와 같은 오류가 발생할 수 있습니다. 이 경우, 클라이언트 컴포넌트의 필요성을 다시 검토하고, 서버 컴포넌트로 변경하는 것이 좋습니다.

자주 묻는 질문

질문1: Next.js에서 layout shift를 방지하는 방법은 무엇인가요?

이미지 컴포넌트에서 width와 height를 명시적으로 설정하거나, placeholder를 사용하여 미리 로딩할 수 있습니다.

질문2: plaiceholder 라이브러리는 어디에서 사용할 수 있나요?

plaiceholder 라이브러리는 서버 컴포넌트에서만 사용할 수 있으며, 클라이언트 컴포넌트에서는 사용할 수 없습니다.

이전 글: 2025 연말정산 기간 및 홈택스 활용법