Next.js 교재 · 4편 · 페이지와 레이아웃

페이지와 레이아웃 — page.tsx·layout.tsx 작성하기

직접 만들며 사이트 골격을 잡는다. 중첩 레이아웃과 children 의 정확한 의미까지.

중첩된 레이아웃이 페이지를 감싸는 컨셉 아이소메트릭 일러스트

3편에서 App Router 의 특수 파일 7가지를 정리했다. 그중 일상 99% 를 차지하는 두 파일 — page.tsx 와 layout.tsx — 를 손으로 직접 만들며 사이트 골격을 잡아본다.

이 두 파일의 관계는 단순하다 — layout 이 page 를 감싼다. 그리고 layout 은 또 다른 layout 으로 감싸질 수 있다. 양파 구조. 이번 편에서 그 양파를 한 겹씩 까본다.

1. page.tsx — 그 URL 의 본문

가장 단순한 페이지부터. app/page.tsx 는 루트 / URL 의 본문이 된다.

// app/page.tsx export default function HomePage() { return ( <main> <h1>안녕, 내 사이트</h1> <p>여기는 홈 페이지입니다.</p> </main> ); }

규칙 3가지면 끝.

  • export default 가 반드시 있어야 한다. 함수 이름은 자유.
  • 반환은 JSX (=React 컴포넌트). HTML 같지만 자바스크립트.
  • 파일 위치(app/page.tsx) 가 곧 URL(/) 이다.

새 페이지를 만들고 싶으면 폴더만 추가. app/about/page.tsx 만들면 /about 즉시 생긴다 — 라우터에 등록하는 코드 따위 없다.

왜 page.tsx 만 라우트? — App Router 는 page · layout · loading 같은 예약 이름만 라우팅에 사용한다. 그래서 같은 폴더에 Button.tsx·utils.ts 를 두어도 라우트가 생기지 않는다. 컴포넌트·유틸이 라우트 폴더 옆에 같이 있어도 안전한 이유.

2. layout.tsx — children 을 감싸는 옷

레이아웃의 핵심은 {children}. 자식 페이지가 들어올 자리.

// app/layout.tsx (루트 레이아웃, 필수) import './globals.css'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="ko"> <body> <header> <nav> <a href="/">홈</a> · <a href="/about">소개</a> </nav> </header> <main>{children}</main> <footer>© 2026 My Site</footer> </body> </html> ); }

이 layout 이 한 번 적용되면 — /, /about, /blog/hello 어디로 가도 같은 헤더와 푸터가 그대로 보인다. 헤더만 적어두면 100개 페이지가 자동 공유.

주의 — 루트 layout 은 반드시 <html> 과 <body> 태그를 포함해야 한다. 다른 layout 은 그냥 <div> 로 감싸도 된다. 루트만 특별.

3. 중첩 레이아웃 — 폴더 단위로 layout 적용

이 게 진짜 강력한 부분. app/blog/layout.tsx 를 만들면 /blog/* 페이지들만 그 layout 으로 감싼다.

// app/blog/layout.tsx export default function BlogLayout({ children, }: { children: React.ReactNode; }) { return ( <div className="blog-wrap"> <aside> <h3>블로그 메뉴</h3> <ul> <li><a href="/blog">최신</a></li> <li><a href="/blog/tags">태그</a></li> </ul> </aside> <section>{children}</section> </div> ); }

이제 /blog/hello 를 열면 — 루트 layout (헤더·푸터) → 블로그 layout (사이드바) → page (글 본문) 의 3겹 양파. /about 은 블로그 layout 을 거치지 않으니 사이드바 없음.

URL거치는 레이아웃결과
/root헤더 + 홈 + 푸터
/aboutroot헤더 + 소개 + 푸터
/blog/helloroot → blog헤더 + 사이드바 + 글 + 푸터

중첩은 깊이 제한 없지만 보통 2~3 단계까지.

4. 메타데이터 — layout 또는 page 에서 export

SEO 의 핵심인 <title>·<meta> 를 어떻게 넣나? <head> 를 직접 만지지 않고 metadata 객체를 export 한다.

// app/about/page.tsx import type { Metadata } from 'next'; export const metadata: Metadata = { title: '소개 | 내 사이트', description: '저는 누구이며 이 사이트는 무엇을 하는지.', }; export default function AboutPage() { return <main>…</main>; }

Next.js 가 빌드 타임에 이 객체를 읽어 <head> 에 자동 삽입한다. page.tsx 에서 export 하면 그 페이지만, layout.tsx 에서 export 하면 그 layout 의 모든 자식 페이지가 기본값을 상속한다. 페이지별 override 가능.

7편 (Metadata API) 에서 OG 이미지·구조화 데이터까지 깊이 다룬다. 이번 편은 "이렇게 넣는다" 정도만.

흔한 함정 — metadata 는 서버 컴포넌트에서만 export 가능. 'use client' 붙은 파일에서 export 하면 무시되거나 에러. 클라이언트 컴포넌트에서 title 을 바꾸려면 document.title 직접 수정 또는 generateMetadata 함수 사용.

5. 실행 흐름 — 요청이 들어오면 무슨 일이

사용자가 /blog/hello 를 누르면 서버에서 일어나는 일.

  1. 라우터가 폴더 매칭 → app/blog/[slug]/page.tsx 찾음.
  2. 위로 올라가며 layout 수집 → blog → root.
  3. 안에서 바깥 순서로 트리 조립 → page → blog → root.
  4. 각 컴포넌트를 서버에서 실행, HTML 문자열로 직렬화.
  5. 브라우저는 HTML 즉시 그리고 인터랙티브 부분만 hydrate.

개발자는 page.tsx·layout.tsx 만 적으면 된다. 이 단순함이 App Router 의 정체.

요약 — 4편 좌표

여기까지 정리. page.tsx 는 그 URL 의 본문, layout.tsx 는 자식을 감싸는 옷. 둘 다 default export 가 핵심. 루트 layout 은 <html>·<body> 필수, 다른 layout 은 자유. 중첩으로 폴더 단위 공통 UI 가능 — /blog/* 만 사이드바 같은 식. SEO 메타데이터는 export const metadata 한 줄. 다음 편에서 폴더 이름 [slug] 로 동적 라우팅을 본다.

다음 편 예고 — 동적·중첩 라우팅

[slug]·(group)·[...all] 폴더 이름 트릭 완전 정리. 5편.

© 2026 주나이테크(주) @JUNAITECH