학습 문서/api reference/functions

generateViewport#

학습 목표#

  • 브라우저의 뷰포트(<meta name="viewport">) 및 테마 색상(<meta name="theme-color">)을 동적으로 설정하는 generateViewport 함수의 역할을 이해한다.
  • 정적 viewport 객체와 동적 generateViewport 함수의 차이를 파악한다.
  • 미디어 쿼리(prefers-color-scheme) 기반의 다크 모드 및 라이트 모드 themeColor를 구성한다.
  • 메타데이터 스트리밍과의 차이점 및 Cache Components 환경에서의 동작 제약을 고려한다.

핵심 개념 및 설명#

Next.js 14부터 뷰포트 및 테마 색상 관련 설정은 metadata 객체에서 분리되어, 정적 viewport 객체 또는 동적 generateViewport 함수를 통해 정의된다.

generateViewport오직 Server Component에서만 지원된다.

app/layout.tsxtsx
import type { Viewport } from 'next'

export const viewport: Viewport = {
  themeColor: [
    { media: '(prefers-color-scheme: light)', color: '#ffffff' },
    { media: '(prefers-color-scheme: dark)', color: '#000000' },
  ],
  width: 'device-width',
  initialScale: 1,
  maximumScale: 1,
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ko">
      <body>{children}</body>
    </html>
  )
}
app/layout.jsjsx
export const viewport = {
  themeColor: [
    { media: '(prefers-color-scheme: light)', color: '#ffffff' },
    { media: '(prefers-color-scheme: dark)', color: '#000000' },
  ],
  width: 'device-width',
  initialScale: 1,
  maximumScale: 1,
}

export default function RootLayout({ children }) {
  return (
    <html lang="ko">
      <body>{children}</body>
    </html>
  )
}
알아두면 좋은 점 (generateViewport 모범 사례 및 내부 동작):
- Server Component 전용: viewport 객체와 generateViewport 함수는 오직 Server Component에서만 export할 수 있다. 한 파일에서 두 개를 동시에 export할 수 없다.
- 정적 vs 동적 선택: 요청 정보(파라미터 등)에 의존하지 않는 정적 뷰포트 설정은 성능 최적화를 위해 비동기 함수 대신 정적 viewport 객체로 export해야 한다.
- 기본 뷰포트 태그 자동 주입: Next.js는 기본적으로 <meta name="viewport" content="width=device-width, initial-scale=1"> 태그를 자동 생성하므로, 기본 동작을 덮어쓰거나 테마 색상을 추가할 때만 명시적으로 설정하면 된다.
- 라우트별 다이나믹 뷰포트 격리: 특정 라우트에서만 다이나믹 generateViewport가 필요하다면, Route Group 및 복수 루트 레이아웃을 사용하여 해당 라우트를 격리함으로써 다른 정적 페이지들이 불필요하게 다이나믹 렌더링으로 전환되는 것을 방지할 수 있다.
- 일반 metadata와 달리 viewport는 브라우저의 초기 화면 레이아웃과 렌더링에 직접적인 영향을 주므로 스트리밍(Streaming)될 수 없다. 따라서 generateViewport가 지연되면 초기 페이지 응답이 블로킹될 수 있다.
- 동적 데이터가 필요하지만 런타임 요청 데이터가 아니라면 'use cache'를 함께 활용하는 것이 권장된다.

주요 뷰포트 필드 (Viewport Fields)#

1. themeColor#

  • 단일 색상 문자열 또는 미디어 쿼리 배열을 지정한다.
layout.tsxtsx
  themeColor: [
    { media: '(prefers-color-scheme: light)', color: '#0ea5e9' },
    { media: '(prefers-color-scheme: dark)', color: '#0f172a' },
  ]

2. colorScheme#

  • 지원 색상 테마 ('light', 'dark', 'light dark').

3. 모바일 뷰포트 크기 및 스케일 제어#

  • width: 'device-width' 또는 숫자.
  • initialScale: 초기 배율 (기본값: 1).
  • maximumScale: 최대 확대 배율.
  • userScalable: 사용자 확대/축소 허용 여부 (boolean).

동적 generateViewport 함수 예제#

app/category/[id]/page.tsxtsx
import type { Viewport } from 'next'

type Props = {
  params: Promise<{ id: string }>
}

export async function generateViewport({ params }: Props): Promise<Viewport> {
  const { id } = await params
  const category = await db.category.findUnique({ where: { id } })

  return {
    themeColor: category?.brandColor || '#000000',
  }
}

export default async function CategoryPage({ params }: Props) {
  const { id } = await params
  return <div>카테고리: {id}</div>
}
app/category/[id]/page.jsjsx
export async function generateViewport({ params }) {
  const { id } = await params
  const category = await db.category.findUnique({ where: { id } })

  return {
    themeColor: category?.brandColor || '#000000',
  }
}

export default async function CategoryPage({ params }) {
  const { id } = await params
  return <div>카테고리: {id}</div>
}

Version History#

버전변경 사항
v14.0.0viewportgenerateViewport 도입 (metadata에서 분리)

예제 및 데모 설계#

  • OS 다크 모드 전환 시 브라우저 상단 주소창 테마 색상이 라이트(#ffffff)와 다크(#000000)로 자동 반응하는지 모바일 및 데스크톱 브라우저에서 검증한다.
  • generateViewport에 카테고리별 브랜드 색상을 매핑하여 라우트 전환에 따른 메타 태그 변화를 확인한다.
  • width=device-width, initial-scale=1 등의 기본 뷰포트 메타 태그가 올바르게 주입되는지 확인한다.

연습 문제#

  1. Next.js에서 다크 모드와 라이트 모드에 따라 브라우저 주소창 테마 색상을 다르게 지정하는 올바른 themeColor 설정 방식은?
  • A. themeColor: 'auto'
  • B. themeColor: [{ media: '(prefers-color-scheme: light)', color: '#fff' }, { media: '(prefers-color-scheme: dark)', color: '#000' }]
  • C. metadata.theme = { dark: '#000', light: '#fff' }
  • D. next.config.jsthemeColor 배열
정답 보기

정답: B 해설: Viewport 인터페이스의 themeColorprefers-color-scheme 미디어 쿼리 조건 객체들의 배열을 지원하여 시스템 테마에 맞는 색상을 동적으로 제공한다.

  1. generateMetadata와 비교하여 generateViewport가 가지는 주요 차이점은?
  • A. generateViewport는 Client Component에서만 실행된다.
  • B. generateViewport는 초기 화면 렌더링에 즉시 필요하므로 메타데이터처럼 스트리밍될 수 없다.
  • C. generateViewport는 비동기 호출을 지원하지 않는다.
  • D. generateViewport는 TypeScript를 지원하지 않는다.
정답 보기

정답: B 해설: 뷰포트는 초기 브라우저 렌더링 영역 크기와 직결되므로 스트리밍으로 지연 주입될 수 없으며 초기 HTML과 함께 즉시 해석되어야 한다.

챕터 요약#

  • generateViewportviewport는 브라우저 뷰포트 및 테마 색상을 정의하는 Server Component 전용 API다.
  • Next.js 14부터 기존 metadata 객체에서 독립되었다.
  • 다크/라이트 모드별 themeColor 설정을 지원한다.
  • 초기 렌더링 특성상 스트리밍이 불가능하므로 불필요한 동적 요청 지연을 피해야 한다.