2025. 2. 24. 14:36ㆍ프론트엔드/Next JS
🔹 1. 기존 Next.js의 글로벌 레이아웃 적용 방식
Next.js에서는 _app.tsx 파일을 수정하여 전역(Global) 레이아웃을 적용할 수 있습니다.
📌 components/global-layout.tsx
import { ReactNode } from "react";
import Link from "next/link";
import style from "./global-layout.module.css";
export default function GlobalLayout({
children,
}: {
children: ReactNode;
}) {
return (
<div className={style.container}>
<header className={style.header}>
<Link href={"/"}>📚 ONEBITE BOOKS</Link>
</header>
<main className={style.main}>{children}</main>
<footer className={style.footer}>제작 @winterlood</footer>
</div>
);
}
위의 GlobalLayout을 _app.tsx에서 적용하면 모든 페이지에서 일관된 레이아웃을 유지할 수 있습니다.
🔹 2. 특정 페이지에서 추가적인 레이아웃 적용 (getLayout 활용)
일반적으로, Next.js에서 글로벌 레이아웃을 적용하면 모든 페이지가 동일한 구조를 따르게 됩니다.
그러나 일부 페이지에서만 특정한 UI(예: 검색 바가 포함된 레이아웃)를 추가하고 싶을 수도 있습니다.
이럴 때는 getLayout 패턴을 활용하여 개별 페이지에 특정 레이아웃을 적용할 수 있습니다.
📌 getLayout을 사용하면
- 페이지별로 다른 레이아웃을 설정할 수 있습니다.
- 기본(Global) 레이아웃을 유지하면서 추가적인 요소(Search bar 등)를 포함할 수 있습니다.
🔹 1. getLayout이 필요한 이유
보통 Next.js 프로젝트에서는 _app.tsx에서 GlobalLayout을 적용하여 모든 페이지에서 동일한 레이아웃을 유지합니다.
return (
<GlobalLayout>
<Component {...pageProps} />
</GlobalLayout>
);
위처럼 하면 모든 페이지에서 GlobalLayout이 적용되지만, 특정 페이지에서만 다른 레이아웃(예: 검색 기능이 있는 SearchableLayout)을 추가해야 한다면?
👉 기존 방식으로는 개별 페이지마다 조건문을 추가해야 하므로 유지보수가 어렵습니다.
따라서, getLayout을 활용하면 각 페이지별로 필요한 레이아웃을 적용할 수 있습니다.
🔹 2. getLayout을 적용한 페이지 예제 (pages/index.tsx)
예제에서는 홈 페이지(index.tsx)에만 SearchableLayout을 추가하고,
그 외의 페이지는 기본 GlobalLayout만 적용되도록 설정합니다.
📌 pages/index.tsx
import SearchableLayout from "@/components/searchable-layout";
import style from "./index.module.css";
import { ReactNode } from "react";
export default function Home() {
return (
<>
<h1 className={style.h1}>인덱스</h1>
<h2 className={style.h2}>H2</h2>
</>
);
}
// 특정 페이지에서만 적용될 레이아웃 정의
Home.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>;
};
✅ 코드 설명
- 기본 페이지(Home) 정의
- <h1>과 <h2> 요소를 포함한 간단한 UI 구조입니다.
- 개별 레이아웃 적용 (getLayout 설정)
- Home.getLayout = (page: ReactNode) => <SearchableLayout>{page}</SearchableLayout>;
- getLayout을 사용하면 현재 페이지(Home)에서만 SearchableLayout을 적용할 수 있습니다.
- SearchableLayout은 검색 창이 포함된 레이아웃이며, index.tsx에서만 사용됩니다.
- getLayout이 없는 페이지는 기본(GlobalLayout)만 적용
- 예를 들어, about.tsx 같은 다른 페이지에는 getLayout이 설정되지 않았으므로 기본 GlobalLayout만 유지됩니다.
🔹 3. _app.tsx에서 getLayout을 적용하는 방식
이제 _app.tsx에서 getLayout을 활용하여 각 페이지에서 레이아웃을 개별적으로 적용할 수 있도록 수정해야 합니다.
📌 pages/_app.tsx
import GlobalLayout from "@/components/global-layout";
import "@/styles/globals.css";
import { NextPage } from "next";
import type { AppProps } from "next/app";
import { ReactNode } from "react";
// NextPageWithLayout 타입 정의
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactNode) => ReactNode;
};
// App 컴포넌트 정의
export default function App({
Component,
pageProps,
}: AppProps & {
Component: NextPageWithLayout;
}) {
// getLayout 함수가 정의되지 않은 경우 기본적으로 페이지를 그대로 반환
const getLayout =
Component.getLayout ?? ((page: ReactNode) => page);
return (
<GlobalLayout>
{getLayout(<Component {...pageProps} />)}
</GlobalLayout>
);
}
🔹 4. _app.tsx 코드 상세 설명
📌 1) NextPageWithLayout 타입 정의
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactNode) => ReactNode;
};
- NextPage 타입을 확장하여 getLayout 속성을 추가합니다.
- getLayout은 페이지별로 특정한 레이아웃을 적용할 수 있도록 하는 함수입니다.
- 이 설정을 통해 각 페이지(Component)에서 선택적으로 레이아웃을 적용할 수 있도록 합니다.
📌 2) getLayout을 적용하는 방식
const getLayout =
Component.getLayout ?? ((page: ReactNode) => page);
- getLayout이 정의된 페이지라면 Component.getLayout을 사용하고,
- 정의되지 않은 경우 기본적으로 페이지를 그대로 반환합니다.
즉, getLayout이 정의된 페이지에서는 getLayout을 사용하여 특정 레이아웃을 적용하고,
getLayout이 없는 페이지에서는 일반적인 레이아웃(GlobalLayout)만 적용됩니다.
📌 3) GlobalLayout 내부에서 getLayout 적용
return (
<GlobalLayout>
{getLayout(<Component {...pageProps} />)}
</GlobalLayout>
);
- 모든 페이지는 GlobalLayout 내부에서 렌더링됩니다.
- 하지만 getLayout이 정의된 페이지라면 추가적인 레이아웃(SearchableLayout 등)이 적용됩니다.
🔹 5. getLayout 적용 흐름 정리
페이지 파일 적용된 레이아웃
| index.tsx | GlobalLayout > SearchableLayout > index.tsx |
| about.tsx | GlobalLayout > about.tsx |
| contact.tsx | GlobalLayout > contact.tsx |
- index.tsx에서는 getLayout을 사용하여 기본 GlobalLayout 위에 SearchableLayout을 추가.
- about.tsx 같은 페이지는 getLayout을 정의하지 않았으므로 기본 GlobalLayout만 적용.
🔹 6. getLayout을 적용하면 얻을 수 있는 장점
✅ 1) 특정 페이지에서만 추가적인 레이아웃 적용 가능
- Home 페이지에는 SearchableLayout을 적용하지만, About 페이지에는 적용되지 않도록 설정할 수 있습니다.
✅ 2) 유지보수 및 확장성 증가
- 기존 방식(조건문을 사용하여 특정 페이지에서만 다른 레이아웃 적용)보다 더 깔끔하고 확장성이 높음.
- 페이지별로 다른 레이아웃이 필요할 때 새로운 레이아웃을 쉽게 추가 가능.
✅ 3) 전역 레이아웃과 페이지별 레이아웃을 조합하여 사용 가능
- GlobalLayout을 유지하면서 특정 페이지에만 추가적인 UI(SearchableLayout 등)를 적용할 수 있음.
🔹 7. 마무리
📌 요약
- getLayout을 사용하여 특정 페이지에서만 추가적인 레이아웃을 적용.
- _app.tsx에서 Component.getLayout이 있는지 확인하고, 페이지별 레이아웃을 개별적으로 적용.
- 기본 GlobalLayout은 유지하면서, 필요에 따라 개별적인 레이아웃(SearchableLayout)을 추가.
- 유지보수 및 확장성이 뛰어난 모듈화된 레이아웃 시스템을 구축 가능.
이제 프로젝트에서 유연한 레이아웃 시스템을 적용하여 더욱 체계적인 개발이 가능합니다! 🚀
🔹 5. 검색 기능이 포함된 SearchableLayout 구현
📌 components/searchable-layout.tsx
import { useRouter } from "next/router";
import { ReactNode, useEffect, useState } from "react";
import style from "./searchable-layout.module.css";
export default function SearchableLayout({
children,
}: {
children: ReactNode;
}) {
const router = useRouter();
const [search, setSearch] = useState("");
const q = router.query.q as string;
useEffect(() => {
setSearch(q || "");
}, [q]);
const onChangeSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearch(e.target.value);
};
const onSubmit = () => {
if (!search || q === search) return; // 페이지 이동 최소화
router.push(`/search?q=${search}`);
};
const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
onSubmit();
}
};
return (
<div>
<div className={style.searchbar_container}>
<input
value={search}
onKeyDown={onKeyDown}
onChange={onChangeSearch}
placeholder="검색어를 입력하세요 ..."
/>
<button onClick={onSubmit}>검색</button>
</div>
{children}
</div>
);
}
✅ 코드 설명
- useRouter를 사용하여 현재 URL의 쿼리 파라미터(q)를 가져옴
- useEffect를 이용하여 검색어 입력창이 URL의 q 값과 동기화되도록 설정
- onChangeSearch: 검색어 입력 시 search 상태 업데이트
- onSubmit: 검색어 입력 후 버튼 클릭 시 /search?q=검색어로 이동
- onKeyDown: Enter 키를 누르면 검색 실행
🔹 6. SearchableLayout을 적용한 결과
이제 Home 페이지(/)에서는 검색 바가 포함된 레이아웃이 적용되지만,
다른 페이지에서는 GlobalLayout만 유지됩니다.
✅ Home.getLayout을 적용한 결과:
--------------------------------------
| 📚 ONEBITE BOOKS (헤더) |
--------------------------------------
| 🔍 검색 바 |
--------------------------------------
| 인덱스 (메인 콘텐츠) |
--------------------------------------
| 제작 @winterlood (푸터) |
--------------------------------------
✅ 다른 페이지 (/about 등):
--------------------------------------
| 📚 ONEBITE BOOKS (헤더) |
--------------------------------------
| About 페이지 (메인 콘텐츠) |
--------------------------------------
| 제작 @winterlood (푸터) |
--------------------------------------
🔹 7. 정리
기능 적용 방식
| Global Layout | _app.tsx에서 GlobalLayout을 적용하여 모든 페이지에서 유지 |
| 개별 페이지 Layout | getLayout을 활용하여 특정 페이지에서만 추가적인 레이아웃 적용 |
| 검색 바 포함 Layout | SearchableLayout을 만들어 Home 페이지에서만 적용 |
'프론트엔드 > Next JS' 카테고리의 다른 글
| 🚀 Next.js 사전 렌더링(Pre-Rendering) 완벽 정리 (2) | 2025.02.24 |
|---|---|
| 📖 Next.js 도서 추천 페이지 상세 설명 (2) | 2025.02.24 |
| Next.js 글로벌 레이아웃(Global Layout) 설정하기 (2) | 2025.02.24 |
| Next.js에서 CSS 적용 방법 총정리 🎨 (2) | 2025.02.24 |
| Next.js API Routes 완벽 정리 (2) | 2025.02.24 |