참고: Next.js 13은 app/ 디렉터리 (베타)가 도입됐습니다. 이 새로운 디렉터리는 레이아웃, 중첩 라우트 및 기본적으로 Server Components를 지원합니다. app/ 내부에서는 레이아웃 내에서 전체 애플리케이션의 데이터를 가져올 수 있으며, 좀 더 세부적인 중첩 레이아웃(겹쳐진 데이터 가져오기와 함께)을 지원합니다.
React 모델은 페이지를 일련의 구성 요소로 분해할 수 있도록 해줍니다. 이 구성 요소 중 많은 것들은 종종 페이지 간에 재사용됩니다. 예를 들어, 모든 페이지에 같은 내비게이션 바와 푸터가 있을 수 있습니다.
// components/layout.js
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
애플리케이션 전체에 하나의 레이아웃만 있을 경우, 커스텀 앱을 생성하고 애플리케이션을 해당 레이아웃으로 감싸면 됩니다. <Layout />
컴포넌트가 페이지를 변경할 때마다 재사용되므로 해당 컴포넌트의 상태(예: 입력 값)가 보존됩니다.
// pages/_app.js
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
여러 레이아웃이 필요한 경우, 페이지마다 getLayout
속성을 추가하여 해당 페이지에 대한 레이아웃에 대한 React 컴포넌트를 반환하면 됩니다.
이렇게 하면 각 페이지마다 레이아웃을 정의할 수 있습니다. 함수를 반환하므로 원하는 경우 복잡한 중첩 레이아웃을 사용할 수 있습니다.
// pages/index.js
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** Your content */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
// 페이지 수준에서 정의된 레이아웃을 사용합니다.
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
페이지 간 이동 시 상태 (입력 값, 스크롤 위치 등)를 유지하기 위해 React 컴포넌트 트리를 유지합니다. 컴포넌트 트리로 React는 어떤 요소가 변경되었는지를 이해하여 상태를 보존할 수 있습니다.
참고: 이 프로세스를 reconciliation 이라고하며, React가 어떤 요소가 변경되었는지를 이해하는 방법입니다.
TypeScript를 사용할 때는, 먼저 페이지를 위한 새로운 유형(type)을 만들어야합니다. 이것은 getLayout 함수를 포함하고 있어야합니다. 그런 다음, 이전에 생성한 유형을 사용하여 AppProps에 대한 새 유형을 만들어야합니다. 이 새로운 타입은 Component 속성을 이전에 만든 타입으로 덮어써야합니다.
// pages/index.tsx
import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'
const Page: NextPageWithLayout = () => {
return <p>hello world</p>
}
Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default Page
// pages/_app.tsx
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
레이아웃 안에서 useEffect
나 SWR
과 같은 라이브러리를 사용하여 클라이언트 측에서 데이터를 가져올 수 있습니다. 이 파일이 페이지가 아니므로 현재 getStaticProps
또는 getServerSideProps를
사용할 수 없습니다.
// components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
const { data, error } = useSWR('/api/navigation', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<>
<Navbar links={data.links} />
<main>{children}</main>
<Footer />
</>
)
}