Pages and Layouts
페이지 라우터는 페이지 개념에 기반한 파일 시스템 기반 라우터를 가지고 있습니다.
pages
디렉토리에 파일을 추가하면 자동으로 라우트로 사용 가능해집니다.
Next.js에서 페이지는 pages
디렉토리의 .js
, .jsx
, .ts
, 또는 .tsx
파일에서 내보내진 React 컴포넌트 (opens in a new tab)입니다. 각 페이지는 파일 이름을 기반으로 한 라우트와 연결됩니다.
예시: 아래와 같이 React 컴포넌트를 내보내는 pages/about.js
를 생성하면, /about
에서 접근할 수 있습니다.
export default function About() {
return <div>About</div>
}
Index routes
라우터는 index
라는 이름의 파일을 디렉토리의 루트로 자동 라우팅합니다.
pages/index.js
→/
pages/blog/index.js
→/blog
Nested routes
라우터는 중첩 파일을 지원합니다. 중첩 폴더 구조를 생성하면 파일들이 여전히 같은 방식으로 자동 라우팅됩니다.
pages/blog/first-post.js
→/blog/first-post
pages/dashboard/settings/username.js
→/dashboard/settings/username
Pages with Dynamic Routes
Next.js는 동적 라우트를 가진 페이지를 지원합니다. 예를 들어, pages/posts/[id].js
라는 파일을 생성하면, posts/1
, posts/2
등에서 접근할 수 있습니다.
동적 라우팅에 대해 더 알아보려면 동적 라우팅 문서를 확인하세요.
Layout Pattern
React 모델을 사용하면 페이지를 여러 컴포넌트로 분해할 수 있습니다. 이 컴포넌트들은 종종 페이지 간에 재사용됩니다. 예를 들어, 모든 페이지에 같은 네비게이션 바와 푸터를 가질 수 있습니다.
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
Examples
Single Shared Layout with Custom App
전체 애플리케이션에 하나의 레이아웃만 있는 경우, 커스텀 앱을 생성하고 애플리케이션을 레이아웃으로 감쌀 수 있습니다. 페이지 변경 시 <Layout />
컴포넌트가 재사용되므로 컴포넌트 상태가 유지됩니다(예: input values).
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
Per-Page Layouts
여러 레이아웃이 필요한 경우, 페이지에 getLayout
속성을 추가하여 레이아웃을 반환할 수 있는 React 컴포넌트를 지정할 수 있습니다. 이를 통해 페이지별로 레이아웃을 정의할 수 있습니다. 함수를 반환하므로 원하는 경우 복잡한 중첩 레이아웃을 가질 수 있습니다.
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {
return (
/** 여기에 내용을 넣으세요 */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default function MyApp({ Component, pageProps }) {
// 가능하다면, 페이지 수준에서 정의된 레이아웃을 사용하세요
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
페이지 간에 이동할 때, 단일 페이지 애플리케이션(SPA) 경험을 위해 페이지 상태(입력 값, 스크롤 위치 등)를 유지 하고자 합니다.
이 레이아웃 패턴은 페이지 전환 사이에 React 컴포넌트 트리가 유지되므로 상태 유지를 가능하게 합니다. 컴포넌트 트리를 통해 React는 어떤 요소가 변경되었는지 파악하여 상태를 보존할 수 있습니다.
알아두면 좋습니다: 이 과정을 재조정 (opens in a new tab)이라고 하며, React가 어떤 요소가 변경되었는지 이해하는 방법입니다.
With TypeScript
타입스크립트를 사용할 때는 먼저 getLayout
함수를 포함하는 페이지의 새 타입을 생성해야 합니다. 그런 다음, 이전에 생성된 타입을 사용하도록 Component
속성을 오버라이드하는 AppProps
의 새 타입을 생성해야 합니다.
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
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
const Page = () => {
return <p>hello world</p>
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
export default Page
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) {
// 가능하다면, 페이지 수준에서 정의된 레이아웃을 사용하세요
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
export default function MyApp({ Component, pageProps }) {
// 가능하다면, 페이지 수준에서 정의된 레이아웃을 사용하세요
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Data Fetching
레이아웃 내에서 useEffect
또는 SWR (opens in a new tab) 같은 라이브러리를 사용하여 클라이언트 측에서 데이터를 가져올 수 있습니다. 이 파일이 페이지가 아니기 때문에 현재 getStaticProps
나 getServerSideProps
는 사용할 수 없습니다.
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 />
</>
)
}