chorock.page
소개글시리즈프로젝트
© 2026 chorock.page
← 목록으로
Next.js 기초 - Page Router · 6/11
← 이전 글다음 글 →
Next.jsweb

Page Router (5) - 레이아웃 최적화

2026.09.14 · 3분 읽기 · 조회 0

목차
공통 레이아웃일반 레이아웃import 방식메서드 방식

공통 레이아웃

먼저 공통 레이아웃을 먼저 만들어보자. header, footer 와 같은 녀석들은 모든 페이지에 적용되므로 _app.tsx 에 만들면 된다.

// _app.tsx
return 
  <header></header>
  <main>
    <Component {...pageProps} />
  </main>
  <footer></footer>

이런 식으로 적용해주어도 될 것이다. 그러나 헤더나 푸터가 복잡해지면 해당 파일이 어지러워지므로 파일을 따로 분리해주는게 더 바람직하다.

/src/components/global-layout.tsx 파일을 만들어서 import 해오는 방식을 사용한다.

// global-layout.tsx
 
export default function GlobalLayout({children}){
  return (
    <header></header>
    <main>
      {children}
    </main>
    <footer></footer>
  )
}

해당 레이아웃을 감싸야 하기 때문에 {children}을 사용한다. {children}의 타입은 {children : ReactNode} 로 지정하면 된다.

// _app.tsx
return 
  <GlobalLayout>
    <Component {...pageProps} />
  </GlobalLayout>

그리고 _app.tsx에서 이렇게 감싸기만 하면 좀 더 깔끔하게 적용할 수 있다.

일반 레이아웃

/와 /search 에서 "검색창" 레이아웃을 보여줘야 한다고 생각해보자.

import 방식

단순히 구현하려면 그냥

// src/components/searchable-layout.tsx
 
export default function SearchableLayout(){
  return <div>검색창</div>
}

이렇게 검색창 컴포넌트를 따로 만들어놓고,

// index.tsx
import SearchableLayout from ""
 
export default function Home(){
  return 
    <>
      <SearchableLayout/>
      <div>메인 내용</div>
    </>
}
// search.tsx
import SearchableLayout from ""
 
export default function Search(){
  return 
    <>
      <SearchableLayout/>
      <div>검색 내용</div>
    </>
}

이렇게 두 곳에서 임포트해오는 방식으로 사용할 수 있을 것이다. 보통은 이런 방식을 많이 사용해 왔을 것이다.

그러나 해당 방식에는 큰 단점이 하나 있다. 바로 페이지 이동 시에 레이아웃 컴포넌트가 반드시 언마운트 된다는 것이다.

해당 방법으로 레이아웃을 적용하면 동일한 레이아웃을 사용하는 페이지로 이동하게 되더라도 무조건 레이아웃이 언마운트 되었다가 다시 마운트되기 때문에 불필요한 렌더링 리소스를 낭비하게 된다.

따라서 만약 레이아웃 컴포넌트 내에서 state를 사용하고 있다면 페이지가 이동될 때 마다 state 값 또한 초기화된다.

이를 방지하는 다른 방법이 있다.

메서드 방식

// src/components/searchable-layout.tsx
 
export default function SearchableLayout({children}){
  return <div>검색창 {children}</div>
}

먼저 검색창 컴포넌트에 {children}을 추가해준다. 타입은 역시 {children : ReactNode} 이다.

// index.tsx
import SearchableLayout from ""
 
export default function Home(){
  return 
      <div>메인 내용</div>
}
 
Home.getLayout = (page : ReactNode) => {
  return <SearchableLayout>{page}<SearchableLayout>
}

그리고 해당 레이아웃을 적용하고 싶은 페이지 "컴포넌트"에 getLayout 이라는 메서드를 정의해준다. 각 컴포넌트는 객체 이므로 메서드 추가가 가능하다.

해당 메서드는 파라미터로 들어온 페이지를 해당 검색창 레이아웃 사이에 끼워넣어주는 기능을 한다.

// search.tsx
import SearchableLayout from ""
 
export default function Search(){
  return 
      <div>검색 내용</div>
}
 
Search.getLayout = (page : ReactNode) => {
  return <SearchableLayout>{page}<SearchableLayout>
}

Search 컴포넌트에도 해당 메서드를 추가해준다.

이제 _app.tsx 에서는 해당 메서드를 사용 가능하다.

// _app.tsx
 
export default function App({Component, pageProps}: AppProps) {
  const getLayout = Component.getLayout ?? ((page: ReactNode) => page)
 
  return (
    <GlobalLayout>
      {getLayout(<Component {...pageProps}/>)}
    </GlobalLayout>
  )
}

Component 에는 현재 페이지의 컴포넌트가 들어 있으므로 /, /search 페이지에서는 아까 정의한 getLayout 메서드를 사용 가능하다.

그 밖의 페이지에서는 해당 메서드가 정의되지 않았으므로 ?? ((page: ReactNode) => page)로 현재 페이지를 그대로 반환하는 예외처리를 추가해준다.

해당 메서드의 파라미터로 <Component {...pageProps}/>를 넣어주면 해당 컴포넌트는 검색 레이아웃에 감싸진다.

이때 타입 에러가 남에 주의한다.

type NextPageWithLayout = NextPage & {
  getLayout?: (page: ReactNode) => ReactNode;
}

다음과 같이 타입을 정의해주고,

export default function App({Component, pageProps}
  : AppProps & {Component : NextPageWithLayout}) {
 
  const getLayout = Component.getLayout ?? ((page: ReactNode) => page)
 
  return (
    <GlobalLayout>
      {getLayout(<Component {...pageProps}/>)}
    </GlobalLayout>
  )
}

이렇게 지정해주면 타입 에러가 발생하지 않는다.

메서드 방식을 사용하면 불필요한 언마운트가 일어나지 않기 때문에 웹을 최적화할 수 있다.

초록
초록

앱과 웹을 만들고, 직접 운영하며 기록합니다.

댓글

giscus로 동작하며 GitHub Discussions에 저장됩니다.

관련 글

Next.js 기초 - App RouterNext.jsweb

App Router (11) - 서버 액션 (Server Action)

2026.09.29·2분 읽기·조회 0
Next.js 기초 - App RouterNext.jsweb

App Router (10) - 우아하게 에러 핸들링하기 (error.tsx)

2026.09.28·3분 읽기·조회 0
Next.js 기초 - App RouterNext.jsweb

App Router (9) - 스트리밍

2026.09.28·3분 읽기·조회 0

목차

공통 레이아웃일반 레이아웃import 방식메서드 방식