Next앱 최적화
시작
- 본 내용은 강의 제공 사이트 유데미 Maximilian Schwarzmüller 강사님의 Next.js 14 & React - The Complete Guide 강의를 듣고 정리하였습니다.
범위
많은 최적화방법 중 이미지 최적화, SEO를 위한 메타데이터 최적화에 대해 알아보겠습니다.
이미지 최적화
NextJS는 특정 이미지 컴포넌트(Image)를 제공하는데 기본적으로 다음과 같은 최적화가 이루어집니다.
- 자동으로 이미지의 크기를 조정하여 최적화된 포맷의 작은 파일을 불러온다.
- 시각적 안정성에 도움을 줌 페이지 로딩을 줄이거나 이미지가 비정상적으로
wrap
에서 빠져나오지 않게 해준다. - 이미지가 화면에 나타날때 스트림으로 보여준다.
이미지 객체
기존 img 태그의 경우 이미지가 프로젝트내에 저장되어 있더라고 src 요소로 경로를 직접 명시해줘야만 했습니다.
<img src={logo.src} alt="Mobile phone with posts feed on it" />
Image 태그로 변경한 후에는 logo라는 이미지 객체를 src 프로퍼티의 값으로 주면 됩니다.
import logo from '@/assets/logo.png';
import Image from 'next/image';
// ...
<Image src={logo} alt="Mobile phone with posts feed on it" />
그럼 이미지 객체는 어떤 정보들이 포함되는지 확인해보겠습니다.
console.log
로 표시해보면 다음과 같은 객체 정보과 출력됩니다.
✓ Compiled in 64ms (507 modules)
{
# 이미지 경로
src: '/_next/static/media/logo.6ad4479c.png',
# 이미지 크기
height: 600,
width: 600,
blurDataURL: '/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo.6ad4479c.png&w=8&q=70',
blurWidth: 8,
blurHeight: 8
}
각 객체의 경우 Image 태그 내에서 어떻게 적용되는지 보기 위해 HTML 페이지로 조회하면 다음과 같은 정보들을 볼 수 있습니다.
<img alt="Mobile phone with posts feed on it" loading="lazy"
width="600" height="600" decoding="async" data-nimg="1" style="color:transparent"
srcset="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo.6ad4479c.png&w=640&q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo.6ad4479c.png&w=1200&q=75 2x"
src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo.6ad4479c.png&w=1200&q=75">
loading="lazy"
프로퍼티는 Image컴포넌트의 기본 설정 옵션으로 이미지를 로드해오는대로 화면에 보여주는 방법이고 srcset
은 다양한 화면 해상도에 맞도록 이미지를 조정해주는 옵션입니다. 둘다 Image태그내 기본적으로 설정됩니다.
width
, height
의 경우 import logo from '@/assets/logo.png
로 가져오는 순간 자동으로 설정되며 사용자가 이 너비, 높이값들을 수정할 수도 있습니다.
이미지 크기 조절하기
<Image
src={logo}
width={100}
height={100}
alt="Mobile phone with posts feed on it"
/>
위와 같은 방법은 권장되는 방법은 아니지만 조작이 가능합니다. 로컬 이미지를 가져올때는 원본 크기를 그대로 유지한 채로 Image 자동 리사이징을 이용하는 방법을 권장합니다.
고정적인 사이즈를 이용하는 사용자가 올리는 게시글의 썸네일 등에는 유효하게 이용할 수 있을겁니다. 하지만 이 또한 권장되는 방법은 아닙니다.
혹은 아래와 같이 sizes
옵션을 설정해 뷰포트 가로 너비의 10%로 이미지를 조정할 수도 있습니다.
<Image
src={logo}
// width={100}
// height={100}
sizes="10vw"
alt="Mobile phone with posts feed on it"
/>
이미지 우선순위 설정하기
Image 컴포넌트의 경우 loading
를 lazy로 기본으로 설정하여 지연로딩을 발생시키는데, 우선순위를 설정해 지연로딩이 아닌 사전 렌더링 방식을 설정할 수 있습니다.
<Image
src={logo}
width={100}
height={100}
priority
alt="Mobile phone with posts feed on it"
/>
priority 프로퍼티의 경우 렌더링된 HTML에서 프로퍼티 fetchpriority="high"
가 추가되며 loading="lazy"
옵션은 제거되는것을 확인할 수 있습니다.
페이지가 로드된 후 이미지를 확실히 보여줘야 하는 경우 이용합니다.
알 수 없는 이미지 로드하기
로컬 이미지가 아닌 사용자가 업로드하는 이미지의 경우 구체적인 정보를 알 수 없죠. 이 문제를 보완해봅시다.
기본적으로 NextJS의 경우 외부 스토리지 서버에서 가져오는 리소스를 보안적으로 막고 있습니다. 때문에 next.config.mjs
에 외부에서 가져오는 이미지 서버는 보안 목록에서 제외해야합니다.
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [{
hostname: 'res.cloudinary.com'
}]
}
};
export default nextConfig;
witdh
, height
의 경우 직접적으로 수정하는 것은 권장되지 않습니다. 이를 위한 보완 방법으로는 Image
태그내 프로퍼티로 fill
(부모 태그 공간 차지)를 추가하고 부모 태그의 크기를 조정하는 방법입니다.
우선 fill
의 경우 position: absolute
가 설정되므로 부모 태그에 position: releative
를 적용합니다.
/* app/global.css */
.post-image {
width: 8rem;
height: 6rem;
position: relative;
}
부모 태그에는 해당 클래스를 명시합니다.
// component/posts.js
<div className="post-image">
<Image
src={post.image}
fill
alt={post.title}
/>
</div>
로더와 클라우디너리 옵션 알아보기
강의에서 사용하는 클라우디너리의 경우 URL 조정 및 여러 추가 옵션이 있습니다. 또한 이미지 조정 옵션도 포함되어 있습니다. NextJS는 클라우디너리와 긴밀히 사용되는 관계로 이런 설정에 최적화되어 있습니다.
NextJS의 loader
를 이용하여 이미지 프로바이더의 클라우디너리의 옵션들을 이용할 수 있습니다. 우선 클라우디너리로부터 오는 객체는 다음과 같이 조회할 수 있습니다.
// component/posts.js
import Image from 'next/image';
function imageLoader(config) {
console.log(config);
return config.src;
}
function Post({ post, action }) {
return (
<article className="post">
<div className="post-image">
<Image
loader={imageLoader}
src={post.image}
fill
alt={post.title}
/>
{/* ... */}
{
src: 'https://res.cloudinary.com/dhwxctrxs/image/upload/v1719109727/nextjs-course-mutations/oxy12jfs1pr1yckgxve4.jpg',
quality: undefined,
width: 1200
}
{
src: 'https://res.cloudinary.com/dhwxctrxs/image/upload/v1719109727/nextjs-course-mutations/oxy12jfs1pr1yckgxve4.jpg',
quality: undefined,
width: 1920
}
{
src: 'https://res.cloudinary.com/dhwxctrxs/image/upload/v1719109727/nextjs-course-mutations/oxy12jfs1pr1yckgxve4.jpg',
quality: undefined,
width: 2048
}
{
src: 'https://res.cloudinary.com/dhwxctrxs/image/upload/v1719109727/nextjs-course-mutations/oxy12jfs1pr1yckgxve4.jpg',
quality: undefined,
width: 3840
}
소스를 담은 src
, 너비값을 가진 width
, quality
프로퍼티가 존재하죠. 클라우드너리의 이미지 조정 옵션은 다음 링크를 참조하면됩니다.
클라우드너리의 경우 URL을 수정해 이미지의 조정이 가능한데, 예시는 다음과 같습니다.
https://res.cloudinary.com/demo/image/upload/c_thumb,g_face,h_200,w_200/r_max/f_auto/woman-blackdress-stairs.png
위 예제를 보면 다음과 같이 loader내 함수에서 URL을 조정하면 됩니다.
// component/posts.js
function imageLoader(config) {
const urlStart = config.src.split('upload/')[0];
const urlEnd = config.src.split('upload/')[1];
const transformations = `w_200,q_${config.quality}`;
return `${urlStart}upload/${transformations}/${urlEnd}`;
}
NextJS에서 fill 프로퍼티의 경우 sizes
를 같이 사용하길 권장하여 warning
으로 표시합니다. 다만 위 이미지 조정을 통해 처리가 끝났으므로 width
, height
를 명시적으로 설정합니다.
// component/posts.js
<div className="post-image">
<Image
loader={imageLoader}
src={post.image}
width={200}
height={120}
alt={post.title}
quality={50}
/>
</div>
페이지 메타데이터
구글 검색엔진 등 크롤링을 통해 SEO로 등록해줄때 메타데이터가 중요하게 작동합니다. 메타데이터의 종류에 대해 알아봅시다.
정적 메타데이터
사전에 미리 렌더링되는 메타데이터 정보로, page.js
, layout.js
에 설정합니다. 반드시 metadata
로 명시하고 export 하면 해당 페이지의 메타데이터로 반영됩니다.
// app/page.js
export const metadata = {
title: 'Latest Posts',
description: 'Browse out latest posts.',
};
위와 같이 설정한 메타데이터는 렌더링된 HTML에 등록됩니다.
<head>
<!-- ... -->
<title>Latest Posts</title>
<meta name="description" content="Browse out latest posts.">
<!-- ... -->
</head>
동적 메타데이터
사용자가 게시한 글의 경우 동적인 데이터들입니다. 이 경우 메타데이터 설정에 대해 알아봅시다.
동적인 데이터가 포함된 메타데이터 정보로, page.js
에 설정합니다. 반드시 generateMetatadata
로 명시하고 export 하면 해당 페이지의 메타데이터로 반영됩니다.
// app/feed/page.js
// 매개변수로는 경로 Param, 검색 필터 searchParam 등으로 메타데이터에 적용할 수도 있다.
export async function generateMetatadata(data) {
const posts = await getPosts();
const numberOfPosts = posts.length;
return {
title: `Browse all out ${numberOfPosts} posts.`,
description: 'Browse all out posts.'
}
}
추가로 layout.js
에 메타데이터를 등록한 경우 동일 경로 page.js
에 기본적인 메타데이터 설정이 없다면 layout.js
의 메타데이터가 적용됩니다.