레이아웃 (Layouts) 활용
공통 UI를 공유하는 레이아웃을 생성하고 관리하는 방법을 학습합니다.
Next.js 기초 입문 11회차: 레이아웃 (Layouts) 활용
이번 회차에서는 Next.js 애플리케이션에서 공통 사용자 인터페이스(UI)를 효율적으로 관리하는 핵심 기능인 레이아웃(Layouts)에 대해 심층적으로 학습합니다. 웹 애플리케이션은 종종 여러 페이지에 걸쳐 동일한 헤더, 푸터, 사이드바와 같은 요소들을 공유합니다. Next.js의 레이아웃 기능은 이러한 공통 UI를 한 곳에서 정의하고 재사용할 수 있도록 하여 코드 중복을 줄이고 유지보수성을 크게 향상시킵니다.
📌 이번 회차 학습 목표
- Next.js 레이아웃의 개념과 필요성을 설명할 수 있습니다.
- 루트 레이아웃(Root Layout)의 역할과 생성 방법을 이해하고 적용할 수 있습니다.
- 중첩 레이아웃(Nested Layouts)을 활용하여 복잡한 UI 구조를 구현할 수 있습니다.
- 레이아웃을 통해 공통 UI 요소를 효과적으로 관리하고 재사용할 수 있습니다.
- 레이아웃 사용 시 발생할 수 있는 일반적인 문제점과 해결책을 파악할 수 있습니다.
📝 개념 설명
Next.js 레이아웃이란?
Next.js 13 버전부터 도입된 App Router에서는 레이아웃(Layouts)이라는 특별한 파일 컨벤션을 제공합니다. 레이아웃은 여러 페이지에 걸쳐 공통적으로 사용되는 UI를 정의하는 React 컴포넌트입니다. 예를 들어, 웹사이트의 모든 페이지에 동일한 내비게이션 바(헤더)와 저작권 정보(푸터)가 필요하다면, 이들을 레이아웃 파일에 정의하여 각 페이지 컴포넌트에서 반복적으로 작성할 필요 없이 자동으로 적용되도록 할 수 있습니다.
- 코드 재사용성 증대: 공통 UI 로직을 한 곳에 집중시켜 중복 코드를 제거합니다.
- 유지보수 용이성: 공통 UI 변경 시 레이아웃 파일만 수정하면 모든 관련 페이지에 적용됩니다.
- 성능 최적화: 페이지 탐색 시 레이아웃 컴포넌트는 다시 렌더링되지 않고, 변경되는 부분(페이지 콘텐츠)만 렌더링되어 성능이 향상됩니다.
- 일관된 사용자 경험: 모든 페이지에서 일관된 디자인과 레이아웃을 제공합니다.
루트 레이아웃 (Root Layout)
모든 Next.js 애플리케이션은 반드시 하나의 루트 레이아웃(Root Layout)을 가져야 합니다. 이 레이아웃은 애플리케이션의 최상위 레벨에 위치하며, 모든 페이지와 중첩 레이아웃에 적용됩니다. 루트 레이아웃은 app 디렉토리의 최상위에 위치한 layout.js (또는 layout.tsx) 파일로 정의됩니다.
루트 레이아웃은 다음 두 가지 중요한 역할을 수행합니다:
- HTML 및 Body 태그 정의:
<html>과<body>태그를 포함하여 문서의 기본 구조를 설정합니다. 이는 Next.js 애플리케이션에서 유일하게 이 태그들을 정의할 수 있는 곳입니다. - 글로벌 스타일 및 메타데이터 적용:
<head>태그에 들어갈<title>,<meta>태그와 같은 메타데이터를 정의하거나, 전역 CSS 파일을 임포트하여 애플리케이션 전체에 적용할 수 있습니다.
<html>과 <body> 태그를 포함해야 합니다.children prop만 업데이트되어 성능 효율성을 높입니다.중첩 레이아웃 (Nested Layouts)
루트 레이아웃 외에도, 특정 경로 세그먼트(폴더)에만 적용되는 중첩 레이아웃을 생성할 수 있습니다. 예를 들어, /dashboard 경로 아래의 모든 페이지에만 특정 사이드바가 필요하다면, app/dashboard/layout.js 파일을 생성하여 해당 사이드바를 정의할 수 있습니다. 이 중첩 레이아웃은 부모 레이아웃(루트 레이아웃 또는 상위 중첩 레이아웃)의 UI를 상속받으며, 자신의 children prop으로 해당 세그먼트의 페이지 컴포넌트를 렌더링합니다.
💡 예제 & 실습
Next.js 프로젝트를 생성하고 레이아웃을 직접 구현해 보겠습니다.
1. Next.js 프로젝트 생성
아직 프로젝트가 없다면, 다음 명령어를 사용하여 새로운 Next.js 프로젝트를 생성합니다.
npx create-next-app@latest my-app
cd my-app
2. 루트 레이아웃 (Root Layout) 구현
app/layout.js 파일을 열어 다음과 같이 수정합니다. 이 파일은 애플리케이션의 모든 페이지에 적용될 최상위 레이아웃을 정의합니다.
// app/layout.js
import './globals.css'; // 전역 스타일시트 임포트
export const metadata = {
title: 'Next.js 레이아웃 학습', // 페이지 제목
description: 'Next.js 레이아웃 기능을 학습하는 예제 애플리케이션입니다.',
};
export default function RootLayout({ children }) {
return (
<html lang='ko'>
<body>
<header style={{ backgroundColor: '#f0f0f0', padding: '1rem', borderBottom: '1px solid #ccc' }}>
<h1>Next.js 레이아웃 데모</h1>
<nav>
<a href='/' style={{ margin: '0 10px' }}>홈</a>
<a href='/dashboard' style={{ margin: '0 10px' }}>대시보드</a>
<a href='/settings' style={{ margin: '0 10px' }}>설정</a>
</nav>
</header>
<main style={{ padding: '20px' }}>
{children}
</main>
<footer style={{ backgroundColor: '#f0f0f0', padding: '1rem', borderTop: '1px solid #ccc', textAlign: 'center', marginTop: '20px' }}>
<p>© 2023 Next.js 학습. 모든 권리 보유.</p>
</footer>
</body>
</html>
);
}
metadata객체는 페이지의<head>태그에 들어갈 메타데이터를 정의합니다.RootLayout컴포넌트는childrenprop을 받습니다. 이children은 해당 레이아웃이 적용될 페이지 또는 하위 레이아웃의 콘텐츠를 나타냅니다.<html>과<body>태그를 포함하며, 전역 헤더와 푸터를 정의하여 모든 페이지에 공통적으로 나타나도록 합니다.
3. 전역 스타일시트 (globals.css) 수정
app/globals.css 파일을 열어 기본적인 스타일을 추가합니다.
/* app/globals.css */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
color: #333;
}
a {
color: #0070f3;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
4. 페이지 컴포넌트 생성
이제 몇 가지 페이지를 생성하여 루트 레이아웃이 어떻게 적용되는지 확인합니다.
홈 페이지 (app/page.js)
// app/page.js
export default function HomePage() {
return (
<div>
<h2>환영합니다!</h2>
<p>이곳은 홈 페이지입니다. 상단의 헤더와 하단의 푸터는 루트 레이아웃에서 제공됩니다.</p>
</div>
);
}
설정 페이지 (app/settings/page.js)
app 디렉토리 안에 settings 폴더를 만들고 그 안에 page.js 파일을 생성합니다.
// app/settings/page.js
export default function SettingsPage() {
return (
<div>
<h2>설정 페이지</h2>
<p>여기서 사용자 설정을 변경할 수 있습니다.</p>
</div>
);
}
5. 중첩 레이아웃 (Nested Layout) 구현
/dashboard 경로에만 적용되는 중첩 레이아웃을 생성해 보겠습니다. 먼저 app 디렉토리 안에 dashboard 폴더를 만들고, 그 안에 layout.js 파일을 생성합니다.
// app/dashboard/layout.js
export const metadata = {
title: '대시보드', // 대시보드 페이지의 제목
description: '사용자 대시보드 페이지입니다.',
};
export default function DashboardLayout({ children }) {
return (
<div style={{ display: 'flex' }}>
<aside style={{ width: '200px', backgroundColor: '#e6f7ff', padding: '1rem', borderRight: '1px solid #b3e0ff' }}>
<h3>대시보드 메뉴</h3>
<ul style={{ listStyle: 'none', padding: 0 }}>
<li style={{ marginBottom: '10px' }}><a href='/dashboard/profile'>프로필</a></li>
<li style={{ marginBottom: '10px' }}><a href='/dashboard/analytics'>분석</a></li>
</ul>
</aside>
<section style={{ flexGrow: 1, padding: '1rem' }}>
{children}
</section>
</div>
);
}
- 이 레이아웃은
/dashboard경로 및 그 하위 경로에만 적용됩니다. childrenprop은/dashboard경로의 페이지 컴포넌트 또는 하위 중첩 레이아웃을 렌더링합니다.- 좌측에 대시보드 전용 사이드바 메뉴를 추가했습니다.
대시보드 페이지 (app/dashboard/page.js)
// app/dashboard/page.js
export default function DashboardPage() {
return (
<div>
<h2>나의 대시보드</h2>
<p>이곳은 대시보드 메인 페이지입니다. 좌측의 메뉴는 대시보드 레이아웃에서 제공됩니다.</p>
</div>
);
}
대시보드 하위 페이지 (app/dashboard/profile/page.js)
app/dashboard 디렉토리 안에 profile 폴더를 만들고 그 안에 page.js 파일을 생성합니다.
// app/dashboard/profile/page.js
export default function ProfilePage() {
return (
<div>
<h3>프로필 정보</h3>
<p>사용자 프로필을 관리하는 페이지입니다.</p>
</div>
);
}
6. 애플리케이션 실행
프로젝트 루트에서 다음 명령어를 실행하여 개발 서버를 시작합니다.
npm run dev
브라우저에서 http://localhost:3000으로 접속하여 각 페이지를 탐색해 보세요.
- 홈 페이지 (
/): 루트 레이아웃의 헤더와 푸터만 보입니다. - 설정 페이지 (
/settings): 루트 레이아웃의 헤더와 푸터만 보입니다. - 대시보드 페이지 (
/dashboard): 루트 레이아웃의 헤더와 푸터, 그리고 대시보드 레이아웃의 사이드바가 함께 보입니다. - 프로필 페이지 (
/dashboard/profile): 루트 레이아웃, 대시보드 레이아웃의 사이드바가 모두 적용되어 보입니다.
이것은 레이아웃이 어떻게 중첩되어 적용되는지를 명확하게 보여줍니다.
⚠️ 자주 틀리는 것 / 주의사항
- 루트 레이아웃의
<html>및<body>태그 누락: 루트 레이아웃(app/layout.js)은 반드시<html>과<body>태그를 포함해야 합니다. 다른 레이아웃이나 페이지에서는 이 태그들을 포함해서는 안 됩니다. childrenprop 누락: 모든 레이아웃 컴포넌트는childrenprop을 받아 렌더링해야 합니다. 그렇지 않으면 해당 레이아웃 내의 페이지 콘텐츠가 표시되지 않습니다.- 레이아웃 파일명 오류: 레이아웃 파일은 반드시
layout.js(또는layout.tsx)로 명명되어야 합니다. 다른 이름은 Next.js가 인식하지 못합니다. - 글로벌 CSS 임포트 위치: 전역 CSS 파일(예:
globals.css)은 루트 레이아웃(app/layout.js)에서만 임포트해야 합니다. 다른 컴포넌트나 레이아웃에서 임포트하면 예상치 못한 동작을 유발할 수 있습니다. use client지시어: 레이아웃은 기본적으로 서버 컴포넌트입니다. 클라이언트 측 상태나 이벤트를 사용해야 하는 경우에만'use client'지시어를 추가해야 합니다. 하지만 레이아웃 자체는 공통 UI를 제공하는 역할이므로, 가능한 서버 컴포넌트로 유지하는 것이 좋습니다.
- 레이아웃(Layouts)은 Next.js에서 여러 페이지에 걸쳐 공통 UI를 정의하고 재사용하는 기능입니다.
- 루트 레이아웃(Root Layout)은
app/layout.js에 정의되며,<html>과<body>태그를 포함하고 모든 페이지에 적용되는 최상위 레이아웃입니다. - 중첩 레이아웃(Nested Layouts)은 특정 경로 세그먼트(폴더) 내의 페이지에만 적용되며, 부모 레이아웃의 UI를 상속받습니다.
- 레이아웃은
childrenprop을 통해 하위 콘텐츠(페이지 또는 다른 레이아웃)를 렌더링합니다. - 레이아웃을 사용하면 코드 재사용성, 유지보수성, 성능 및 일관된 사용자 경험을 향상시킬 수 있습니다.
🔗 다음 회차 예고
이번 회차에서는 Next.js의 강력한 레이아웃 기능을 통해 공통 UI를 효율적으로 관리하는 방법을 학습했습니다. 다음 12회차에서는 데이터 페칭 (Data Fetching)에 대해 학습할 예정입니다. Next.js 애플리케이션에서 다양한 방식으로 데이터를 가져오고 렌더링하는 방법을 심층적으로 다루어, 동적인 웹 페이지를 구현하는 데 필요한 핵심 지식을 습득하게 될 것입니다.