next js 중첩 레이아웃 프로젝트를 하던중 탭을 이용해 페이지 내에서 페이지를 전환?해야 할게 생겼다. React에서는 react-router-dom의 outlet을 이용해 쉽게 만들 수 있었는데 next에서는 어떤식으로 만드는지 궁금해졌다.
next 공식 문서를 먼저 찾아본다..
넥스트 공식문서
If you need multiple layouts, you can add a property getLayout to your page, allowing you to return a React component for the layout. This allows you to define the layout on a per-page basis. Since we’re returning a function, we can have complex nested layouts if desired.
여러 레이아웃이 필요한 경우, 페이지에 getLayout 속성을 추가하여 레이아웃에 대한 React 컴포넌트를 반환할 수 있습니다. 이를 통해 페이지 단위로 레이아웃을 정의할 수 있습니다. 함수를 반환하기 때문에 원하는 경우 복잡한 중첩 레이아웃을 가질 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 } /> ) }
역시 공식문서는 봐도 어렵다
그냥 똑같이 만들어 본다.
일단 파일은 이렇게 만들었다.
Header를 담고 있는 전체 Layout, 특정 페이지에만 적용될 NestedLayout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import React from 'react' import Header from './Header' interface LayoutProps { children : React .ReactNode } const Layout = ({ children }: LayoutProps ) => { return ( <div > <Header /> {children} </div > ) } export default Layout
레이아웃 컴포넌트를 만들고
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import '@/styles/globals.css' import type { AppProps } from 'next/app' import Layout from '@/components/Layout' import { NextPage } from 'next' export type NextPageWithLayout <P = {}, IP = P> = NextPage <P, IP > & { getLayout?: (page: React.ReactElement ) => React .ReactNode } type AppPropsWithLayout = AppProps & { Component : NextPageWithLayout } export default function App ({ Component, pageProps }: AppPropsWithLayout ) { const getLayout = Component .getLayout || (page => page) return <Layout > {getLayout(<Component {...pageProps } /> )}</Layout > }
페이지 컴포넌트 내에 getLayout 메서드가 있으면 그것을 사용하고, 없으면 입력으로 받은 페이지를 그대로 반환한다.
헤더가 적용된 layout
about 페이지에는 SideNav를 넣어 /about/1
, /about/2
이런식으로 라우팅을 할 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import React from 'react' import SideNav from './SideNav' interface NestedLayoutProps { children : React .ReactNode } const NestedLayout = ({ children }: NestedLayoutProps ) => { return ( <div className ="flex" > <SideNav /> {children} </div > ) } export default NestedLayout
NestedLayout을 정의하고
1 2 3 4 5 6 7 8 9 10 11 12 import React from 'react' import type { NextPageWithLayout } from '../_app' import NestedLayout from '@/components/NestedLayout' const About : NextPageWithLayout = () => { return <div > About</div > } About .getLayout = function getLayout (page: React.ReactElement ) { return <NestedLayout > {page}</NestedLayout > } export default About
About페이지를 NestedLayout으로 감싸준다. getLayout 메소드가 있으므로 _app
에서 Component.getLayout
이 Return된다.
그리고 dynamic 페이지에도 똑같이 적용해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import NestedLayout from '@/components/NestedLayout' import React from 'react' import type { NextPageWithLayout } from '../../_app' import { GetStaticPaths , GetStaticProps } from 'next' import { dummy } from '../../../../dummy' interface NestedAboutProps { data?: { id : number text : string } } const NestedAbout : NextPageWithLayout = ({ data }: NestedAboutProps ) => { return <div > {data?.text}</div > } NestedAbout .getLayout = function getLayout (page: React.ReactElement ) { return <NestedLayout > {page}</NestedLayout > } export default NestedAbout export const getStaticPaths : GetStaticPaths = async () => { const paths = dummy.map (item => ({ params : { id : String (item.id ) } })) return { paths, fallback : false , } } export const getStaticProps : GetStaticProps = async ctx => { const id = ctx.params ?.id const data = dummy.find (item => item.id === Number (id)) return { props : { data, }, } }
더미 데이터를 이용해 static페이지를 생성 해보았는데 잘 적용 된다.