[ Optimizing ] Metadata

차차·2023년 5월 23일
0

Next Docs

목록 보기
28/34
post-thumbnail

Next.js에는 SEO웹 공유성을 향상시키기 위해 애플리케이션의 메타데이터(예: HTML head 요소 내부의 metalink 태그)를 정의할 수 있는 Metadata API가 있다.


애플리케이션에 메타데이터를 추가하는 두 가지 방법이 있다.

  1. 구성 기반 메타데이터
    layout.js 또는 page.js 파일에서 정적 메타데이터 객체동적 generateMetadata 함수를 내보낸다.

  2. 파일 기반 메타데이터
    라우트 세그먼트에 정적 또는 동적으로 생성된 특수 파일을 추가한다.


이러한 두 가지 옵션 모두 Next.js가 페이지에 대한 관련 <head> 요소를 자동으로 생성한다.

또한 ImageResponse 생성자를 사용하여 동적 OG 이미지를 생성할 수도 있다.



정적 메타데이터

정적 메타데이터를 정의하려면 layout.js 또는 정적 page.js 파일에서 Metadata 객체를 내보낸다.

// layout.tsx or page.tsx

import { Metadata } from 'next';
 
export const metadata: Metadata = {
  title: '...',
  description: '...',
};
 
export default function Page() {}


동적 메타데이터

동적 값을 필요로하는 메타데이터를 가져오기 위해 generateMetadata 함수를 사용할 수 있다.

// app/products/[id]/page.tsx

import { Metadata, ResolvingMetadata } from 'next';
 
type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent?: ResolvingMetadata,
): Promise<Metadata> {
  // read route params
  const id = params.id;
 
  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json());
 
  // optionally access and extend (rather than replace) parent metadata
  const previousImages = (await parent).openGraph?.images || [];
 
  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  };
}
 
export default function Page({ params, searchParams }: Props) {}

💡 알아두면 좋은 점

  • 정적 및 동적 메타데이터(generateMetadata를 통한)는 Server Components에서만 지원된다.

  • 라우트를 렌더링할 때, Next.js는 generateMetadata, generateStaticParams, layout, pageServer Components 전체에서 동일한 데이터에 대한 중복된 fetch 요청을 자동으로 제거한다. fetch를 사용할 수 없는 경우 React 캐시를 사용할 수 있다.

  • Next.js는 generateMetadata 내부에서 데이터를 가져오는 것이 완료될 때까지 UI를 클라이언트로 스트리밍하기 전까지 대기한다. 이는 스트리밍 응답의 첫 부분에 <head> 태그가 포함되도록 보장한다.



파일 기반 메타데이터

이러한 특수 파일들은 메타데이터로 사용할 수 있다.

  • favicon.ico, apple-icon.jpg, 그리고 icon.jpg

  • opengraph-image.jpgtwitter-image.jpg

  • robots.txt

  • sitemap.xml

이러한 파일들은 정적 메타데이터로 사용하거나 코드로 프로그래밍적으로 생성할 수 있다.



동작

파일 기반 메타데이터는 더 높은 우선순위를 가지며, 구성 기반 메타데이터보다 우선하여 적용된다.


기본 필드 (Default Fields)

경로에서 메타데이터를 정의하지 않더라도 항상 추가되는 두 가지 기본 메타 태그가 있다.

  • meta charset 태그는 웹 사이트의 문자 인코딩을 설정한다.

  • meta viewport 태그는 다른 기기에 맞게 웹 사이트의 뷰포트 너비와 확대/축소를 설정한다.


<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

디폴트 viewport 메타 태그를 덮어쓸 수 있다.


순서 (Ordering)

메타데이터는 순서대로 평가되며, 최종 페이지.js 세그먼트에 가장 가까운 세그먼트에서부터 시작하여 루트 세그먼트까지 내려간다. 예를 들면 다음과 같다.

  1. app/layout.tsx (루트 레이아웃)

  2. app/blog/layout.tsx (중첩된 블로그 레이아웃)

  3. app/blog/[slug]/page.tsx (블로그 페이지)


병합 (Merging)

평가 순서를 따라 동일한 경로의 여러 세그먼트에서 내보낸 메타데이터 객체는 얕은 병합을 통해 경로의 최종 메타데이터 출력을 형성하기 위해 함께 병합된다. 중복된 키는 순서에 따라 교체된다.

이는 이전 세그먼트에서 정의된 openGraphrobots와 같은 중첩 필드를 가진 메타데이터가 마지막 세그먼트에 의해 덮어씌워진다는 것을 의미한다.


1. 필드 덮어쓰기

// app/layout.ts

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
};
// app/blog/page.js

export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
};
 
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
  • app/layout.jstitleapp/blog/page.jstitle에 의해 대체된다.

  • app/blog/page.js에서 openGraph 메타데이터를 설정하기 때문에 app/layout.js의 모든 openGraph 필드가 대체된다. openGraph.description이 없는 것에 주목해야한다.


일부 중첩된 필드를 세그먼트 간에 공유하면서 다른 필드를 덮어쓰고 싶다면 별도의 변수로 분리할 수 있다.

// app/shared-metadata.js

export const openGraphImage = { images: ['http://...'] };
// app/page.js

import { openGraphImage } from './shared-metadata';
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
};
// app/about/page.js

import { openGraphImage } from '../shared-metadata';
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
};

위의 예시에서는 app/layout.jsapp/about/page.js 간에 OG 이미지가 공유되고 제목은 다르다.


2. 필드 상속

// app/layout.ts

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
};
// app/about/page.js

export const metadata = {
  title: 'About',
};
 
// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
  • app/layout.jstitleapp/about/page.jstitle에 의해 대체된다.

  • app/about/page.js에서 openGraph 메타데이터를 설정하지 않았기 때문에 app/layout.js의 모든 openGraph 필드가 상속된다.



동적 이미지 생성

ImageResponse 생성자를 사용하면 JSX와 CSS를 사용하여 동적 이미지를 생성할 수 있다. 이는 오픈 그래프 이미지, Twitter 카드 등의 소셜 미디어 이미지를 생성하는 데 유용하다.

ImageResponseEdge Runtime을 사용하며, Next.js는 자동으로 캐시된 이미지에 올바른 헤더를 추가하여 성능을 향상시키고 다시 계산을 줄이는 데 도움을 준다.


사용하려면 next/server에서 ImageResponse를 가져올 수 있다.

// app/about/route.jsx

import { ImageResponse } from 'next/server';
 
export const runtime = 'edge';
 
export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    },
  );
}

ImageResponseRoute Handlers 및 파일 기반 메타데이터를 포함한 다른 Next.js API와 잘 통합된다. 예를 들어, opengraph-image.tsx 파일에서 ImageResponse를 사용하여 빌드 시간에 또는 요청 시간에 동적으로 오픈 그래프 이미지를 생성할 수 있다.

ImageResponse는 flexbox와 절대 위치 지정을 포함한 일반적인 CSS 속성, 사용자 정의 폰트, 텍스트 줄 바꿈, 가운데 정렬 및 중첩 이미지를 지원한다.


💡  알아두면 좋은 점

  • ImageResponse는 HTML과 CSS를 PNG로 변환하기 위해 @vercel/og, SatoriResvg를 사용한다.

  • Edge Runtime만 지원된다. 기본 Node.js 런타임은 작동하지 않는다.

  • flexbox와 CSS 속성의 일부만 지원된다. 고급 레이아웃(예: display: grid)은 작동하지 않는다.

  • 최대 번들 크기는 500KB다. 번들 크기에는 JSX, CSS, 폰트, 이미지 및 기타 에셋이 포함된다. 제한을 초과하는 경우 에셋의 크기를 줄이거나 런타임에서 가져오는 방식을 고려해야 한다.

  • ttf, otfwoff 폰트 형식만 지원된다. 폰트 구문 분석 속도를 극대화하기 위해 woff보다는 ttf 또는 otf가 선호된다.



JSON-LD

JSON-LD는 구조화된 데이터를 검색 엔진이 내용을 이해하는 데 사용할 수 있는 형식이다. 예를 들어, 사람, 이벤트, 조직, 영화, 책, 레시피 및 여러 종류의 개체를 설명하는 데 사용할 수 있다.

JSON-LD에 대한 현재 권장 사항은 구조화된 데이터를 layout.js 또는 page.js 컴포넌트에서 <script> 태그로 렌더링하는 것이다. 예를 들면 다음과 같다.

// app/products/[id]/page.tsx

export default async function Page({ params }) {
  const product = await getProduct(params.id);
 
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  };
 
  return (
    <section>
      {/* Add JSON-LD to your page */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  );
}

Google의 Rich Results Test 또는 일반적인 Schema Markup Validator를 사용하여 구조화된 데이터를 유효성 검사하고 테스트할 수 있다.

schema-dts와 같은 커뮤니티 패키지를 사용하여 TypeScript로 JSON-LD를 작성할 수 있다.



[ 출처 ]
https://nextjs.org/docs/app/building-your-application/optimizing/metadata

profile
나는야 프린이

0개의 댓글