SPACE RUMI

Hi, I am rumi. Let's Splattack!

[STUDY] 스터디/NEXT.js Docs

[Optimizing] Next 공식문서 번역 / Optimizing - Metadata / Next Metadata

백루미 2023. 5. 19. 21:57
반응형

메타데이터

Next.js에는 SEO 및 웹 공유성을 개선하기 위해 애플리케이션 메타데이터(예: HTML 헤드 요소 내부의 metalink 태그)를 정의하는 데 사용할 수 있는 메타데이터 API가 있습니다.

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

구성 기반 메타데이터: 정적 메타데이터 객체 또는 generateMetadata function을 layout.js 또는 page.js 파일로 내보냅니다.
파일 기반 메타데이터: 정적 또는 동적으로 생성된 특수 파일을 route 세그먼트에 추가합니다.
이 두 가지 옵션을 모두 사용하면 Next.js가 페이지에 대한 관련 <head> 요소를 자동으로 생성합니다. ImageResponse 생성자를 사용하여 동적 OG(OpenGrpah) 이미지를 생성할 수도 있습니다.

 

정적 메타데이터

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

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

사용 가능한 모든 옵션은 API 문서를 참조하세요.

 

동적 메타데이터

generateMetadata 함수를 사용하여 동적 값이 필요한 메타데이터를 가져올 수 있습니다.

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) {}

사용 가능한 모든 매개변수는 API 레퍼런스를 참조하세요.

알아두면 좋습니다:

  • generateMetadata를 통한 정적 메타데이터와 동적 메타데이터는 모두 서버 컴포넌트에서만 지원됩니다.
  • route를 렌더링할 때 Next.js는 generateMetadata, generateStaticParams, 레이아웃, 페이지 및 서버 컴포넌트에서 동일한 데이터에 대한 가져오기 요청을 자동으로 중복 제거합니다. fetch를 사용할 수 없는 경우 React cache를 사용할 수 있습니다.
  • Next.js는 클라이언트에 UI를 스트리밍하기 전에 generateMetadata 내부의 데이터 fetch가 완료될 때까지 기다립니다. 이렇게 하면 스트리밍된 응답의 첫 부분에 <head> 태그가 포함되도록 보장합니다.

 

파일 기반 메타데이터

메타데이터로 사용할 수 있는 특수 파일은 다음과 같습니다:

- favicon.ico, apple-icon.jpg, icon.jpg
- opengraph-image.jpgtwitter-image.jpg
- robots.txt
- sitemap.xml

이러한 파일을 정적 메타데이터에 사용하거나 코드를 사용하여 프로그래밍 방식으로 생성할 수 있습니다.
구현 방법과 예제는 메타데이터 파일 API 참조동적 이미지 생성을 참조하세요.

 

동작

파일 기반 메타데이터가 더 높은 우선순위를 가지며 모든 구성 기반 메타데이터보다 우선합니다.

기본 필드

route가 메타데이터를 정의하지 않더라도 항상 추가되는 두 가지 기본 메타 태그가 있습니다:

메타 문자 집합 태그는 웹사이트의 문자 인코딩을 설정합니다.
메타 뷰포트 태그는 웹사이트의 뷰포트 너비와 배율을 설정하여 다양한 장치에 맞게 조정합니다.

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

참고: 기본 viewport 메타 태그를 덮어쓸 수 있습니다.

 

순서

메타데이터는 root 세그먼트부터 시작하여 최종 page.js 세그먼트에 가장 가까운 세그먼트까지 순서대로 평가됩니다. 예를 들어

  1. app/layout.tsx(루트 레이아웃)
  2. app/blog/layout.tsx(중첩된 블로그 레이아웃)
  3. app/blog/[slug]/page.tsx(블로그 페이지)

 

병합

평가 순서에 따라 동일한 경로의 여러 세그먼트에서 내보낸 메타데이터 개체를 얕게 병합하여, 라우트 최종 메타데이터 출력을 형성합니다. 중복 키는 순서에 따라 교체됩니다.

즉, 이전 세그먼트에서 정의된 openGraphrobot과 같이 중첩된 필드가 있는 메타데이터는 마지막 세그먼트에서 해당 필드를 덮어쓰고 정의합니다.

필드 덮어쓰기

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
};
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
};
 
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

위의 예시에서는

  • app/layout.js의 title은 app/blog/page.js에서 title로 대체됩니다.
  • app/blog/page.js가 openGraph 메타데이터를 설정하기 때문에 app/blog/page.js의 모든 openGraph 필드가 app/blog/page.js에서 대체됩니다. openGraph.description이 없다는 점에 유의하세요.
    세그먼트 간에 중첩된 일부 필드를 공유하면서 다른 필드를 덮어쓰려면 해당 필드를 별도의 변수로 가져올 수 있습니다:
// app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] };
// app/pages.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',
  },
};

위의 예시에서 OG 이미지는 app/layout.jsapp/about/page.js 간에 공유되지만 title은 다릅니다.

 

필드 상속

// app/layout.js

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.js title app/about/page.js title 대체됩니다.

app/about/pages.js openGraph 메타데이터를 설정하지 않기 때문에 app/layout.js 모든 openGraph 필드는 app/about/page.js에서 상속됩니다.

 

동적 이미지 생성

ImageResponse 생성자를 사용하면 JSX 및 CSS를 사용하여 동적 이미지를 생성할 수 있습니다. 이는 오픈 그래프 이미지, 트위터 카드 등과 같은 소셜 미디어 이미지를 생성할 때 유용합니다.

ImageResponse는 edge 런타임을 사용하며, Next.js는 엣지에서 캐시된 이미지에 올바른 헤더를 자동으로 추가하여 성능을 개선하고 재계산을 줄입니다. 

이를 사용하려면 next/server에서 ImageResponse를 가져오면 됩니다:

import { ImageResponse } from 'next/server';
// app/about/rout.jsx

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,
    },
  );
}

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

ImageResponse는 플렉스박스 및 절대 위치 지정, 사용자 정의 글꼴, 텍스트 줄 바꿈, 가운데 정렬, 중첩 이미지 등 일반적인 CSS 속성을 지원합니다. 지원되는 CSS 속성 전체 목록을 참조하세요.

알아두면 좋습니다:

  • 예제는 vercel OG 플레이그라운드에서 확인할 수 있습니다.
  • ImageResponse는 @vercel/og, Satori 및 Resvg를 사용하여 HTML과 CSS를 PNG로 변환합니다.
  • 엣지 런타임만 지원됩니다. 기본 Node.js 런타임은 작동하지 않습니다.
  • 플렉스박스와 CSS 속성의 하위 집합만 지원됩니다. 고급 레이아웃(예: 표시: 그리드)은 작동하지 않습니다.
  • 최대 번들 크기는 500KB입니다. 번들 크기에는 JSX, CSS, 글꼴, 이미지 및 기타 모든 자산이 포함됩니다. 제한을 초과하는 경우 에셋의 크기를 줄이거나 런타임에 가져오는 것을 고려하세요.
  • 글꼴 형식은 ttf, otf, woff만 지원됩니다. 글꼴 구문 분석 속도를 최대화하려면 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용 리치 결과 테스트 또는 일반 스키마 마크업 유효성 검사기를 사용하여 구조화된 데이터의 유효성을 검사하고 테스트할 수 있습니다.

schema-dts와 같은 커뮤니티 패키지를 사용하여 TypeScript로 JSON-LD를 입력할 수 있습니다:

import { Product, WithContext } from 'schema-dts';
 
const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
};
반응형