[Next] <Image>으로 레이아웃 시프트 예방 (feat. plaiceholder, skeloton)
웹사이트의 퍼포먼스를 최적화하는 과정에서 중요한 요소 중 하나는 레이아웃 시프트(Layout Shift)를 최소화하는 것입니다.
이는 사용자 경험에 직접적인 영향을 미치며, 특히 콘텐츠가 갑작스럽게 이동하는 현상은 사용자에게 불편을 초래합니다. Next.js는 이러한 문제를 해결하기 위해 Image 컴포넌트를 제공합니다.
Layout Shift란?
레이아웃 시프트는 웹 페이지가 로드되는 동안 콘텐츠가 갑작스럽게 이동하는 현상을 말합니다.
이는 특히 이미지나 광고가 로드될 때 자주 발생합니다.
예를 들어, 텍스트가 먼저 로드되고 나중에 이미지가 로드되면, 이미지가 로드되면서 텍스트가 아래로 밀리는 현상이 발생할 수 있습니다.
Next.js의 Image 컴포넌트
Next.js는 이미지 최적화 및 레이아웃 시프트 문제를 해결하기 위해 next/image 모듈을 제공합니다.
이 컴포넌트는 다양한 최적화 기능을 통해 성능을 개선하고 레이아웃 시프트를 최소화합니다.
- 자동 사이즈 조정: Image 컴포넌트는 이미지를 로드하기 전에 그 크기를 고정할 수 있습니다. 이를 통해 레이아웃 시프트를 방지할 수 있습니다.
- 레이지 로딩: 스크롤될 때 이미지를 로드하여 초기 로딩 속도를 개선합니다.
- 반응형 이미지: 다양한 해상도와 디바이스에 맞추어 이미지를 자동으로 최적화합니다.
다음은 Next.js의 Image 컴포넌트를 사용하는 예시입니다.
import Image from 'next/image';
function HomePage() {
return (
<div>
<h1>Next.js Image 컴포넌트 데모</h1>
<p>이 텍스트는 이미지 아래에 위치합니다.</p>
<Image
src="/example.jpg" // 이미지 경로
alt="Example Image" // 이미지 설명
width={500} // 이미지 너비
height={300} // 이미지 높이
layout="responsive" // 반응형 레이아웃
/>
<p>이 텍스트는 이미지 아래에 위치합니다.</p>
</div>
);
}
export default HomePage;
위의 예시에서는 Image 컴포넌트를 사용하여 이미지를 로드합니다.
width와 height 속성을 설정하여 이미지의 크기를 고정함으로써 레이아웃 시프트를 방지할 수 있습니다. layout="responsive"를 통해 이미지는 반응형으로 조절됩니다.
아래에서 더 자세하게 알 수 있으니 참고하세욥!
https://nextjs.org/docs/app/building-your-application/optimizing/images
Optimizing: Images | Next.js
Optimize your images with the built-in `next/image` component.
nextjs.org
https://nextjs.org/docs/app/api-reference/components/image
Components: <Image> | Next.js
Optimize Images in your Next.js Application using the built-in `next/image` Component.
nextjs.org
이렇게 레이아웃 시프트를 해결한 것 같은데,
ux를 더 좋게 하기 위해서
Image 컴포넌트가 제공하는
blur, blurDataUrl을 사용해보려고 한다.
<Image> 컴포넌트 attr ---> blur, blurDataUrl
placeholder="blur"
이 옵션을 사용하면 이미지가 완전히 로드되기 전까지 흐릿한 블러(blur) 효과를 적용하여 사용자에게 이미지를 미리 볼 수 있게 합니다.
이는 사용자가 이미지가 로드되는 동안 빈 공간을 보는 대신 블러 처리된 이미지를 볼 수 있게 해주어 더 나은 시각적 경험을 제공합니다.
blurDataURL
blurDataURL은 블러 효과를 적용할 때 사용할 저해상도 이미지를 지정합니다.
이 저해상도 이미지는 매우 작아서 빠르게 로드될 수 있으며, 이를 통해 블러 효과를 더욱 자연스럽게 연출할 수 있습니다.
사용예시
import Image from 'next/image';
function HomePage() {
return (
<div>
<h1>Next.js Image 컴포넌트의 블러 효과 데모</h1>
<p>이미지 로드 중 블러 효과를 확인해보세요.</p>
<Image
src="/example.jpg" // 실제 이미지 경로
alt="Example Image" // 이미지 설명
width={500} // 이미지 너비
height={300} // 이미지 높이
placeholder="blur" // 블러 효과 사용
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..." // 저해상도 블러 이미지
/>
</div>
);
}
export default HomePage;
저해상도 블러 이미지를 생성하는 방법은 여러 가지가 있지만, 가장 간단한 방법은 이미지 최적화 도구를 사용하여 저해상도 이미지를 만들고 이를 base64로 인코딩하는 것입니다.
예를 들어, 다양한 온라인 도구나 이미지 편집 소프트웨어를 사용하여 저해상도 이미지를 생성한 후, 이를 base64로 변환하여 blurDataURL에 사용하면 됩니다.
여기서 좋은 라이브러리가 있습니다.
Plaiceholder
Plaiceholder는 Next.js와 같은 프레임워크에서 이미지 로딩 시 더 나은 사용자 경험을 제공하기 위해 사용되는 이미지 처리 도구입니다.
특히 이미지가 로드될 때 발생할 수 있는 시각적 불편함을 줄여줍니다.
주요 기능
- 저해상도 블러 이미지 생성: Plaiceholder는 원본 이미지에서 저해상도 블러 이미지를 자동으로 생성합니다. 이 블러 이미지는 이미지가 완전히 로드되기 전까지 표시됩니다.
- Base64 인코딩: 생성된 저해상도 이미지를 Base64 형식으로 인코딩하여 blurDataURL로 쉽게 사용할 수 있습니다.
주의할 점
plaiceholder는 브라우저에서는 동작하지 않는다.
Plaiceholder is a server-side library. It will not work in the browser.
따라서 Next.js 13에서 plaiceholder 라이브러리를 쓰려면 서버 컴포넌트에서 써야 한다.
설치
npm install @plaiceholder/next
사용
next.config.mjs 옵션 수정
import withPlaiceholder from "@plaiceholder/next";
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'loremflickr.com',
port: '',
pathname: '/**',
},
],
},
};
// export default nextConfig;
export default withPlaiceholder(nextConfig);
getBase64파일 만들기 --> Base64 인코딩 해주기
import { getPlaiceholder } from "plaiceholder";
export const getBase64 = async (src: string) => {
const buffer = await fetch(src).then(async (res) =>
Buffer.from(await res.arrayBuffer())
);
const {
metadata: { height, width },
...plaiceholder
} = await getPlaiceholder(buffer, { size: 10 });
return {
...plaiceholder,
img: { src, height, width },
};
};
위 함수를 사용할 컴포넌트 만들기
import {getBase64} from '../_lib/getBase64';
import Image from "next/image";
async function ImageWithPlaceholder({ src }: { src: string }) {
const { base64, img } = await getBase64(src);
return (
<Image
src={src}
alt={src}
fill
sizes='100%'
placeholder="blur"
blurDataURL={base64}
/>
);
}
export default ImageWithPlaceholder;
컴포넌트 import해서 적용하기
<ImageWithPlaceholder src={`${user.image[0]}`} />
이렇게 해서 완료!
했는데 클라이언트 컴포넌트에서 사용하면
에러가 발생해서...
이런저런 해결책을 찾아보다 결국 그 에러를 풀지못해서
클라이언트에선 shadcn/ui의 스켈레톤 컴포넌트를 사용했다..
음.. onload 되거나 해서 정확히 하려고했는데
슬라이드라 그런지 제대로 작동하지 않아서,
기본 배경으로 뒤에깔고 이미지가 내려오는 방식으로 했다.
<Carousel opts={{loop: true, dragThreshold: 5 }} setApi={setApi} className={style.carousel}>
<CarouselContent>
{users[userNumber].image.map((img, index) => (
<CarouselItem key={index} onClickCapture={onClick} className='aaa'>
{/* <ImageWithPlaceholder src={`${img}`} /> */}
<div className='relative h-full'>
<Skeleton className='w-full h-full absolute top-0 left-0' />
<img className='w-full h-full block rounded-lg absolute top-0 left-0 z-10' src={img} alt='image' />
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className='left-[15px] hidden sm:flex z-20' />
<CarouselNext className='right-[15px] hidden sm:flex z-20' />
</Carousel>
이 부분은 차후에 개선해야겠다.
다른 이미지 리스트처럼 blur할수 있으면 그렇게 하도록!
참조!
Plaiceholder
Beautiful image placeholders, without the hassle.
plaiceholder.co
https://haruisshort.tistory.com/302
[Next.js] Next.js 13 <Image> plaiceholder 라이브러리 적용기
Next.js 13으로 프로젝트를 하던 중 이미지가 로딩될 때 layout shift가 생기는 걸 발견했다. layout shift의 원인 Next.js에서 제공하는 컴포넌트를 사용하려면 무조건 너비와 높이를 지정해서 넘겨줘야 한
haruisshort.tistory.com
https://www.tezify.com/how-to/avoid-large-layout-shifts/
Avoid Large Layout Shifts Resulting from Loading Images
Table of Contents What it means? Identify what is causing the layout shift Fix layout shifts from images When image dimensions are fixed & known When image dimensions are unknown but it's aspect ratio is fixed When image dimensions and aspect ratio are not
www.tezify.com
이미지 로딩 애니메이션(feat. 저화질, skeleton loading)
이미지를 로딩하는 동안, 그 공백을 채우는 방법은 여러가지가 있다. 이번 시간에는 저화질의 이미지 혹은 skeleton 로딩을 띄우는 방법을 알아보자! 1. 저화질 이미지 먼저 띄우기 A. 원본의 저화
mong-blog.tistory.com
'next.js' 카테고리의 다른 글
[Next & Node] 프론트와 백엔드간의 쿠키 공유하기 (Cookie) (0) | 2024.06.27 |
---|---|
[Next] 유저정보 수정 후 바로 서버 세션 업데이트하기 (update server session) (0) | 2024.06.18 |
[Next] UseSelectedLayoutSegment / UsePathname Hook (0) | 2024.05.13 |
[Next] App Router의 폴더들 (0) | 2024.05.13 |
[Next] getServerSideProps 와 getStaticProps (0) | 2024.05.07 |
댓글