# Next.js(App)에서 사용하기 URL: /docs/theming/next-app Next.js App Router 환경에서 @vapor-ui/core를 설정하고, 서버 사이드 렌더링(SSR)의 이점을 활용하여 깜빡임 없는 동적 테마를 구현하는 방법을 안내합니다. ## 개요 Next.js의 App Router에서 서버 사이드 렌더링(SSR)과 스트리밍 기능을 활용하면, `@vapor-ui/core`는 매우 효율적으로 깜빡임 없는(FOUC-free) 동적 테마를 구현할 수 있습니다. 핵심은 **`ThemeScript`** 컴포넌트에 있습니다. 이 컴포넌트는 React가 클라이언트에서 실행되기 전에, 서버가 보내는 초기 HTML에 FOUC 방지 스크립트를 미리 주입하는 역할을 합니다. ## 설정 방법 #### 1단계: 패키지 설치 ```bash npm install @vapor-ui/core ``` #### 2단계: 테마 설정 파일 생성 (선택사항이지만 권장) 프로젝트의 중앙에서 테마 설정을 관리하기 위해 별도 파일을 생성합니다. ```ts // lib/theme.config.ts import { createThemeConfig } from '@vapor-ui/core'; export const themeConfig = createThemeConfig({ appearance: 'system', // 'light', 'dark', or 'system' radius: 'full', scaling: 1.0, storageKey: 'my-next-app-theme', }); ``` #### 3단계: `layout.tsx`에 Provider 및 스크립트 설정 최상위 `app/layout.tsx` 파일에서 `ThemeProvider`와 `ThemeScript`를 설정합니다. ```tsx // app/layout.tsx import { themeConfig } from '@/lib/theme.config'; import { ThemeProvider, ThemeScript } from '@vapor-ui/core'; import type { Metadata } from 'next'; // 설정 파일 경로 // 필수: 스타일 시트를 임포트합니다. import '@vapor-ui/core/dist/styles.css'; export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app', }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( // 'suppressHydrationWarning'은 필수입니다. // 서버와 클라이언트의 초기 테마 속성 불일치로 인한 경고를 방지합니다. {/* FOUC 방지를 위한 스크립트입니다. 태그의 최상단에 위치하는 것을 권장합니다. */} {/* 동적 테마 관리를 위한 프로바이더 */} {children} ); } ``` > **suppressHydrationWarning은 왜 필요한가요?** > > 서버는 `localStorage`에 접근할 수 없으므로 항상 기본 테마로 HTML을 렌더링합니다. 하지만 클라이언트는 `localStorage`에 저장된 테마(예: 'dark')를 읽어 `` 태그의 속성을 변경합니다. 이로 인해 서버 렌더링 결과와 클라이언트의 첫 렌더링 결과가 달라져 하이드레이션 오류가 발생할 수 있습니다. `suppressHydrationWarning`은 이 특정 속성에 대한 React의 경고를 안전하게 무시하도록 합니다. ## 클라이언트 컴포넌트에서 테마 사용하기 `useTheme` 훅은 클라이언트 상태에 의존하므로, 이 훅을 사용하는 컴포넌트는 반드시 **`"use client"`** 지시어를 사용해야 합니다. 또한, 하이드레이션 불일치를 안전하게 피하려면 컴포넌트가 클라이언트에 마운트된 후에 테마에 의존적인 UI를 렌더링하는 것이 좋습니다. ```tsx // components/ThemeToggleButton.tsx 'use client'; import { useEffect, useState } from 'react'; import { useTheme } from '@vapor-ui/core'; // components/ThemeToggleButton.tsx export function ThemeToggleButton() { const [mounted, setMounted] = useState(false); const { colorTheme, setTheme } = useTheme(); // useEffect는 클라이언트에서만 실행되므로, // 이 훅을 사용해 컴포넌트가 마운트되었음을 확인합니다. useEffect(() => { setMounted(true); }, []); // 마운트되기 전에는 UI를 렌더링하지 않거나 스켈레톤 UI를 보여줍니다. // 이렇게 하면 서버 렌더링 결과와 클라이언트 첫 렌더링 결과가 일치하게 됩니다. if (!mounted) { // 버튼 크기에 맞는 스켈레톤 UI 등을 보여줄 수 있습니다. return ( ); } const toggleTheme = () => { setTheme({ colorTheme: colorTheme === 'light' ? 'dark' : 'light', }); }; return ; } ``` 이 패턴을 통해 서버 렌더링의 이점을 유지하면서 안전하게 동적 테마 기능을 사용할 수 있습니다.