import Head from "next/head";
import Image from "next/image";
const src =
"https://i.namu.wiki/i/hJ1KU8XudUoCnAJaFnOGYDQSPOilQhEmve4Sv7usDD4mBCPf1bWEbEiN2y6HYaua2tZ9CfgV0ulrE4g4JOmOKGfrkfXxq028P3i_W-2cwK7jtlxeoHVGK7Dsu9wY1kDITXDsMGpxGj0QkpMlxWXfvw.webp";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Image src={src} alt="Image" width={300} height={300} />
</>
);
}
위의 소스 코드와 같이 간단한 이미지를 next/image로 렌더링한다고 생각해 봅시다.
서버에 이미지 리소스를 요청할 때 w=384라는 쿼리 파라미터가 붙은 것을 확인할 수 있습니다.
width가 384인 이미지를 달라고 요청한 거죠.
파일의 크기는 17614바이트네요.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
remotePatterns: [
{
protocol: "https",
hostname: "i.namu.wiki",
},
],
},
};
export default nextConfig;
Next.js 공식 문서를 확인해보면 imageSizes, deviceSizes에 기본값이 위처럼 설정돼 있습니다.
breakPoints를 지정해 줄 수 있는 거죠.
그래서 width를 300으로 지정해 줬을 때 384사이즈에 해당하는 이미지를 요청하게 됩니다.
https://nextjs.org/docs/pages/api-reference/components/image
import Head from "next/head";
import Image from "next/image";
const src =
"https://i.namu.wiki/i/hJ1KU8XudUoCnAJaFnOGYDQSPOilQhEmve4Sv7usDD4mBCPf1bWEbEiN2y6HYaua2tZ9CfgV0ulrE4g4JOmOKGfrkfXxq028P3i_W-2cwK7jtlxeoHVGK7Dsu9wY1kDITXDsMGpxGj0QkpMlxWXfvw.webp";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div style={{ position: "relative", width: "700px", height: "700px" }}>
<Image src={src} alt="Image" layout="fill" />
</div>
</>
);
}
코드를 위처럼 작성했을 땐 어떨지 확인해 볼까요?
1080크기의 이미지를 요청하고 있습니다.
크기도 43334바이트로 늘어났습니다.
layout속성을 fill로 지정해 주었을 때 image의 크기를 viewport크기에 맞춰 들고오기 때문에 그렇습니다.
이미지의 width를 직접 지정해 주기 어려운 경우,
예를 들어보면 width: "100%" 처럼 화면에 꽉 차게 이미지를 렌더링하고 싶은 경우가 있을 텐데
그런 경우에 부모 요소의 넓이를 100%로 지정하고 Image태그의 layout을 fill로 지정해 주게 됩니다.
실제로 뷰포트의 크기를 확인해 보면 832px이고,
832px은 828보다 큰값, 1080보다 작은값 이기에 1080사이즈의 이미지를 요청하게 됩니다.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
remotePatterns: [
{
protocol: "https",
hostname: "i.namu.wiki",
},
],
},
};
export default nextConfig;
이번엔 이미지의 크기를 각각 100vw, 50vw로 렌더링하고 싶은 경우 아래와 같이 코드를 짜게 될 겁니다.
import Head from "next/head";
import Image from "next/image";
const src =
"https://i.namu.wiki/i/hJ1KU8XudUoCnAJaFnOGYDQSPOilQhEmve4Sv7usDD4mBCPf1bWEbEiN2y6HYaua2tZ9CfgV0ulrE4g4JOmOKGfrkfXxq028P3i_W-2cwK7jtlxeoHVGK7Dsu9wY1kDITXDsMGpxGj0QkpMlxWXfvw.webp";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div style={{ position: "relative", width: "100vw", height: "300px" }}>
<Image
src={src}
alt="Image"
layout="fill"
style={{ objectFit: "cover" }}
/>
</div>
<div style={{ position: "relative", width: "50vw", height: "150px" }}>
<Image
src={src}
alt="Image"
layout="fill"
style={{ objectFit: "cover" }}
/>
</div>
</>
);
}
100vw 크기로 렌더링한 이미지의 경우 1080사이즈의 이미지를 요청했고,
50vw 크기로 렌더링한 이미지의 경우도 1080사이즈의 이미지를 요청했습니다.
이유는 Image태그의 sizes속성 기본값이 100vw이기 때문인데,
한 마디로 layout:fill을 주게 될 경우 무조건 뷰포트 크기에 따라 이미지를 요청한다는 말이 됩니다.
이것을 방지하기 위해서 sizes프롭을 지정해 주어야 하는데,
아래처럼 코드를 변경해 보겠습니다.
import Head from "next/head";
import Image from "next/image";
const src =
"https://i.namu.wiki/i/hJ1KU8XudUoCnAJaFnOGYDQSPOilQhEmve4Sv7usDD4mBCPf1bWEbEiN2y6HYaua2tZ9CfgV0ulrE4g4JOmOKGfrkfXxq028P3i_W-2cwK7jtlxeoHVGK7Dsu9wY1kDITXDsMGpxGj0QkpMlxWXfvw.webp";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div style={{ position: "relative", width: "100vw", height: "300px" }}>
<Image
src={src}
alt="Image"
layout="fill"
sizes="100vw"
style={{ objectFit: "cover" }}
/>
</div>
<div style={{ position: "relative", width: "50vw", height: "150px" }}>
<Image
src={src}
alt="Image"
layout="fill"
sizes="50vw"
style={{ objectFit: "cover" }}
/>
</div>
</>
);
}
큰 이미지의 경우 그대로 1080사이즈의 이미지를 요청했고,
작은 이미지의 경우 640사이즈의 이미지를 요청했네요.
다시한번 breakPoints를 확인해 봅시다.
지금 뷰포트의 크기는 832px입니다.
그래서 100vw의 경우 1080사이즈의 이미지를 요청했고,
50vw의 경우 832/2 = 416px이 됩니다.
그래서 384~640의 범위에 해당하기에 640사이즈의 이미지를 요청하게 됩니다.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
remotePatterns: [
{
protocol: "https",
hostname: "i.namu.wiki",
},
],
},
};
export default nextConfig;
마지막으로 20vw의 경우를 확인해보면
import Head from "next/head";
import Image from "next/image";
const src =
"https://i.namu.wiki/i/hJ1KU8XudUoCnAJaFnOGYDQSPOilQhEmve4Sv7usDD4mBCPf1bWEbEiN2y6HYaua2tZ9CfgV0ulrE4g4JOmOKGfrkfXxq028P3i_W-2cwK7jtlxeoHVGK7Dsu9wY1kDITXDsMGpxGj0QkpMlxWXfvw.webp";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div style={{ position: "relative", width: "100vw", height: "300px" }}>
<Image
src={src}
alt="Image"
layout="fill"
sizes="100vw"
style={{ objectFit: "cover" }}
/>
</div>
<div style={{ position: "relative", width: "20vw", height: "150px" }}>
<Image
src={src}
alt="Image"
layout="fill"
sizes="20vw"
style={{ objectFit: "cover" }}
/>
</div>
</>
);
}
832/5 = 166.4px 이기에
128~256의 범위에 해당하는 256사이즈의 이미지를 요청하게 됩니다.
layout:fill을 사용할 경우 같은 이미지를 어떤 컴포넌트 내에서 렌더링하냐에 따라 적절한 sizes를 지정해 주어야
렌더링할 크기보다 한참 큰 이미지를 불러오는 일이 일어나지 않습니다.
물론 이미지를 크기에 맞게 변환하여 내려주는 것 자체가 리소스가 드는 일이기 때문에,
breakPoints를 과도하게 설정해주는 것이 좋지는 않습니다.
대체로 필요한 경우를 제외하고, 기본값을 사용하면 될 듯합니다.
당연하게도 원본 이미지가 600px이라면, 1080사이즈, 1200사이즈로 요청하더라도
원본 이미지가 내려오게 됩니다.
'웹 > 프론트엔드' 카테고리의 다른 글
Next.js 14 + tailwind에서 다크모드 설정하기 (0) | 2024.10.03 |
---|---|
Next.js + jest 설정 시 react/swiper 해석이 안되는 이슈 (0) | 2024.04.22 |
[Next.js] NextJS Boilerplate 제작기 (1) | 2024.03.07 |
[Babylon.js] Gimzo, GimzoManager를 pub/sub 패턴으로 관리하기 (0) | 2023.06.25 |
Zustand 현명하게 사용하기 (불필요한 리렌더링 막기) (10) | 2023.06.12 |
댓글