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

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

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

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

3편에서 App Router 의 특수 파일 7가지를 정리했다. 그중 일상 99% 를 차지하는 두 파일 — page.tsxlayout.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