ㅇㅇㅈ Blog

프론트엔드 수행중

0%

next-js-nested-layout

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) {
// Use the layout defined at the page level, if available
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페이지를 생성 해보았는데 잘 적용 된다.