본문 바로가기

NEXT.js

[Next.js] layout.js 와 templete.js 의 차이점

 

1. Layout.js

  • 공유 UI를 여러 페이지에서 재사용
  • 페이지 전환시 리렌더링되지 않음
  • 한번 마운트된 후 계속 유지되는 특성
  • 상태가 유지됨
  • 헤더, 네비게이션 바와 같은 지속적으로 유지되어야 하는 UI 요소에 사용
  • 리렌더링되지 않아 성능상 이점

2. Templete.js

  • 매 페이지 전환시 새로운 인스턴스 생성
  • 상태가 유지되지 않음 (not preserved)
  • 매번 새로 마운트됨
  • 페이지 뷰 추적 , 페이지 전환 애니메이션, 입력 값 초기화가 필요한 경우에 사용
  • 컴포넌트 격리가 필요할 때도 사용
  • 템플릿은 매번 새로 마운트되어 메모리 관리 필요

 

// Layout.tsx

'use client'

export default function Layout({ children }) {
  console.log('Layout 마운트됨!') // 최초 한 번만 실행
  
  useEffect(() => {
    console.log('Layout useEffect 실행')
    return () => {
      console.log('Layout 언마운트됨') // 앱을 완전히 나갈 때만 실행
    }
  }, [])

  return <div>{children}</div>
}

 

// Template.tsx

'use client'

export default function Template({ children }) {
  console.log('Template 마운트됨!') // 페이지 전환마다 실행됨
  
  useEffect(() => {
    console.log('Template useEffect 실행')
    return () => {
      console.log('Template 언마운트됨') // 페이지 전환시마다 실행됨
    }
  }, [])

  return <div>{children}</div>
}

 

만약 로딩 스피너를 적용하거나 방문자수를 카운트 하는 기능이 있다고 할 경우,

템플릿에서는 새로 마운트 될 때마다, 스피너가 표시되거나 방문자 수가 늘어나겠지만

레이아웃은 맨 처음 마운트 되는 1회에만 적용된다게, 두 양식의 차이라고 생각하시면 됩니다.

 

 

Suspense Boundaries 도 마찬가지 입니다.

 

// layout.tsx

export default function Layout({ children }) {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      {children}
    </Suspense>
  )
}

 

- 최초 로드시에만 폴백(LoadingSpinner)이 표시됨

- 페이지 전환시에는 이전 페이지 내용이 유지되다가 새 컨텐츠로 교체

 

// templete.tsx

export default function Template({ children }) {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      {children}
    </Suspense>
  )
}

 

- 페이지 전환할 때마다 폴백(LoadingSpinner)이 표시됨

- 매번 새로운 인스턴스가 생성되므로 로딩 상태가 항상 보임

 

 

 

 

3. 랜더링 순서

 

랜더링 순서로는 아래의 구조를 보시면 됩니다.

<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

 

 

1. Layout이 가장 바깥쪽에서 감싸고
2. Template이 그 안에서 children을 감싸는 구조
3. Template은 고유한 key를 받아 페이지 전환시마다 새로운 인스턴스 생성하게 됩니다 

 

app/
├── layout.js      // 루트 레이아웃
├── template.js    // 루트 템플릿
├── page.js        // 홈 페이지
└── dashboard/
    ├── layout.js  // 중첩된 레이아웃
    ├── template.js // 중첩된 템플릿
    └── page.js    // dashboard 페이지

 

 

이런 구조라고 하면

 

1. Root Layout
2. Root Template
3. Page Layout (있는 경우)
4. Page Template (있는 경우)
5. Page

 

순으로 랜더링 되게 됩니다.

 

https://nextjs.org/blog/layouts-rfc

 

Layouts RFC

Nested routes and layouts, client and server routing, React 18 features, and designed for Server Components.

nextjs.org

 

 

4. 사용 예시

 

1. 분석(Analytics) + 공통 UI
Layout: 네비게이션, 헤더, 푸터 등 공통 UI
Template: 페이지별 방문 추적, 사용자 행동 분석


2. 애니메이션 + 지속적 UI
Layout: 항상 표시되는 UI 요소
Template: 페이지 전환 애니메이션


3. 상태 초기화 + 공통 기능
Layout: 전역 상태, 인증 상태 등 유지
Template: 페이지별 폼 데이터, 필터 초기화


4. 성능 최적화 + 사용자 경험
Layout: 한 번 로드된 컴포넌트 재사용
Template: 페이지별 최적화 (스크롤 위치 등)

 

물론 templete은 Next.js의 권장 패턴으로 반드시 사용해야하는 것은 아니며,

page 에서 직접 구현하거나, 레퍼 컴포넌트, 라이터 이벤트를 활용하며 구현도 가능합니다.

 

하지만 팀 협업시 의도의 명확성, 코드 구조의 일관성을 생각하면 가능하면

프로젝트의 요구사항과 팀의 선호도에 반하는 게 아니라면 사용하는 게 좋다고 생각합니다