1. Page Router란?
- pages/ 폴더 기반으로 자동 라우팅이 설정됩니다.
- pages/ 내부의 파일명과 경로가 곧 URL 경로가 됩니다.
- App Router (app/) 방식과 공존할 수 있으며, 프로젝트에 따라 선택적으로 사용할 수 있습니다.
✅ Next.js 13 이후
- app/ 폴더를 사용하면 App Router (서버 컴포넌트 기반) 방식
- pages/ 폴더를 사용하면 Page Router (기존 방식) 유지 가능
- 프로젝트 요구사항에 따라 app/과 pages/를 함께 사용할 수도 있음
2. Next.js 프로젝트 시작하기
1) 프로젝트 생성
npx create-next-app@14 my-next-app
cd my-next-app
npx: 최신 버전의 Node 패키지를 설치 없이 실행할 수 있도록 도와주는 도구
create-next-app: 새로운 Next.js 프로젝트를 생성
@14: Next.js 14 버전 지정
2) 프로젝트 실행
개발자 모드로 실행하려면:
npm run dev
프로덕션 모드로 실행하려면:
npm run build
npm run start
애플리케이션은 http://localhost:3000에서 실행됩니다.
3. Next.js 프로젝트 구조와 라우팅
1) 프로젝트 구조
my-next-app/
├── public/ # 정적 파일(이미지, 아이콘 등)
├── src/ # 소스 코드 (Next.js 13 이후 권장 구조)
│ ├── app/ # App Router (Next.js 13 이상)
│ ├── components/ # 재사용 가능한 컴포넌트
│ ├── pages/ # Page Router 방식
│ ├── styles/ # 스타일 파일
│ ├── api/ # API 라우트
├── .gitignore # Git 무시할 파일 설정
├── package.json # 프로젝트 설정 및 의존성 관리
├── next.config.js # Next.js 설정 파일
├── README.md # 프로젝트 설명
2) 페이지 기반 라우팅
Next.js에서는 pages/ 폴더 안의 파일이 자동으로 라우팅됩니다.
파일 경로 | 라우팅 경로 |
pages/index.tsx | / |
pages/book/index.tsx | /book |
pages/book/[id].tsx | /book/:id (동적 라우트) |
pages/book/[...id].tsx | /book/:id, /book/:id/:id2, /book/:id/:id2/:id3 (다중 필수 동적 라우트) |
pages/book/[[...id]].tsx | /book, /book/:id, /book/:id/:id2 (다중 선택적 동적 라우트) |
4. 주요 파일
1) _document.tsx
Next.js에서 _document.tsx 파일을 사용하여 HTML 문서의 기본 구조를 설정할 수 있습니다.
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="ko">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
2) _app.tsx
- Next.js에서 _app.tsx는 모든 페이지의 진입점 역할을 합니다. 즉, 모든 페이지가 렌더링될 때 _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";
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactNode) => ReactNode;
};
export default function App({ Component, pageProps }: AppProps & { Component: NextPageWithLayout }) {
const getLayout = Component.getLayout ?? ((page) => <GlobalLayout>{page}</GlobalLayout>);
return getLayout(<Component {...pageProps} />);
}
- 매개변수: { Component, pageProps }
- Component: 현재 렌더링되는 페이지 컴포넌트 (pages/ 폴더 안의 파일)
- pageProps: 각 페이지에서 전달받는 props (데이터 패칭 결과 등)
AppProps & { Component: NextPageWithLayout }
export default function App({ Component, pageProps }: AppProps & { Component: NextPageWithLayout }) {
✅ Component는 NextPageWithLayout 타입을 가지므로, getLayout을 선택적으로 포함할 수 있음.
✅ getLayout을 활용하면 페이지마다 다른 레이아웃을 적용할 수 있습니다.
5. 레이아웃 적용
1) getLayout 동작 방식
const getLayout = Component.getLayout ?? ((page: ReactNode) => page);
- Component.getLayout이 존재하면 사용
- 없으면 기본적으로 ((page: ReactNode) => page)를 사용하여 페이지 그대로 반환
const getLayout = Component.getLayout ?? ((page: ReactNode) => page);
✅ 이렇게 하면 모든 페이지에서 getLayout을 필수로 지정하지 않아도 되고, 기본값을 적용할 수 있음.
2) return 부분: <GlobalLayout> 적용
return <GlobalLayout>{getLayout(<Component {...pageProps} />)}</GlobalLayout>;
- getLayout(<Component {...pageProps} />)
- Component(현재 페이지)를 getLayout 함수에 전달
- getLayout이 설정된 경우, 해당 레이아웃으로 감싸짐
- 없으면 ((page) => page)가 기본값이므로 그대로 반환됨
✅ GlobalLayout을 사용하는 페이지 (/)
// pages/index.tsx
import GlobalLayout from "@/components/global-layout";
import { ReactNode } from "react";
export default function Home() {
return <h1>홈 페이지</h1>;
}
// getLayout을 정의하여 GlobalLayout 적용
Home.getLayout = (page: ReactNode) => <GlobalLayout>{page}</GlobalLayout>;
- getLayout이 존재하므로:이 부분은 <GlobalLayout><Component {...pageProps} /></GlobalLayout>이 됨.
- tsx 복사편집 getLayout(<Component {...pageProps} />)
- 그리고 _app.tsx에서 또 <GlobalLayout>을 감싸고 있으므로 중첩됨 😵💫⏩ 결과적으로 <GlobalLayout>이 두 번 적용됨! 🚨
- tsx 복사편집 return <GlobalLayout>{getLayout(<Component {...pageProps} />)}</GlobalLayout>;
3) GlobalLayout 중첩 문제 해결
GlobalLayout을 무조건 감싸면 getLayout이 적용된 페이지에서도 중첩될 수 있다.
이를 해결하려면 getLayout이 없을 때만 GlobalLayout을 적용하도록 수정해야 한다.
✅ 수정된 _app.tsx
export default function App({ Component, pageProps }: AppProps & { Component: NextPageWithLayout }) {
const getLayout = Component.getLayout ?? ((page) => <GlobalLayout>{page}</GlobalLayout>);
return getLayout(<Component {...pageProps} />);
}
이제 getLayout이 없을 때만 기본적으로 GlobalLayout을 적용한다.
✅ 결과
- / 페이지는 GlobalLayout을 직접 적용하므로 _app.tsx에서 다시 감쌀 필요 없음.
- /search 페이지 같은 경우 SearchableLayout을 적용하면 GlobalLayout 없이 동작.
4) getLayout을 적용한 페이지 예제
(1) / 페이지 (기본 홈)
// pages/index.tsx
import GlobalLayout from "@/components/global-layout";
import { ReactNode } from "react";
export default function Home() {
return <h1>홈 페이지</h1>;
}
// GlobalLayout 적용
Home.getLayout = (page: ReactNode) => <GlobalLayout>{page}</GlobalLayout>;
✅ getLayout이 설정되었으므로 _app.tsx에서 중복 적용되지 않음.
(2) /search 페이지 (검색)
// pages/search/index.tsx
import SearchableLayout from "@/components/searchable-layout";
import { ReactNode } from "react";
export default function Page() {
return <h1>검색 페이지</h1>;
}
// SearchableLayout 적용
Page.getLayout = (page: ReactNode) => <SearchableLayout>{page}</SearchableLayout>;
✅ _app.tsx에서는 GlobalLayout을 적용하지 않으므로 SearchableLayout만 적용됨
6. 스타일 적용법
CSS Module 사용 예제
// index.tsx
import style from "./index.module.css";
export default function Home() {
return (
<>
<h1 className={style.h1}>인덱스</h1>
<h2 className={style.h2}>H2</h2>
</>
);
}
/* index.module.css */
.h1 {
color: red;
}
.h2 {
color: blue;
}
7. 페이지 이동 방법
1) Link 컴포넌트 사용
<Link href="/">홈으로 이동</Link>
2) router.push() 사용 (프로그래매틱 라우팅)
import { useRouter } from "next/router";
export default function Page() {
const router = useRouter();
return <button onClick={() => router.push("/test")}>Go to Test</button>;
}
✅ router.push()를 사용하면 프로그래매틱한 라우팅이 가능합니다.
3) 프리페칭 (Prefetching)
Next.js에서는 기본적으로 Link 컴포넌트가 보이는 경우, 해당 페이지를 미리 로드합니다.
<Link href="/about" prefetch>About 페이지로 이동</Link>
프리페칭을 비활성화하려면:
<Link href="/about" prefetch={false}>About 페이지로 이동</Link>
✅ 프리페칭을 활용하면 페이지 전환 속도를 빠르게 만들 수 있습니다.
8. 404 오류 페이지
404 페이지는 기본적으로 pages/404.tsx 파일을 생성하여 설정할 수 있습니다.
// 404.tsx
export default function Page() {
return <div>존재하지 않는 페이지입니다.</div>;
}
✅ next.config.js에서 trailingSlash: true를 설정하면 /404/ 경로를 명확히 처리할 수 있습니다.
module.exports = {
trailingSlash: true,
};
'Frontend > Next.js' 카테고리의 다른 글
[Next.js/Page Router] 4. SEO 설정 및 Next.js 프로젝트 배포하기 (0) | 2025.02.05 |
---|---|
[Next.js/Page Router] 3. ISR (Incremental Static Regeneration) (0) | 2025.02.04 |
[Next.js/Page Router] 3. SSG (Static Site Generation) (0) | 2025.02.03 |
[Next.js/Page Router] 2. SSR (Server Side Rendering) (0) | 2025.02.02 |
[Next.js] 0. Next.js 개요 (1) | 2025.01.21 |