Section 3. Page Router 핵심 정리(1)

OlMinJe·2025년 9월 25일

Next.js

목록 보기
2/20
post-thumbnail

인프런 "한 입 크기로 잘라먹는 Next.js" 수강

1. Page Router를 소개합니다.

📌 Page Router란?

리액트에서 자주 사용하던 리액트 라우터처럼, 특정 조건을 기준으로 웹 서비스 내 페이지를 분할하고 이동을 처리할 수 있도록 해주는 기능이다.

Next.js에서는 pages 폴더 구조를 기반으로 페이지 라우팅을 지원한다.

  • 파일명 뿐만 아니라 폴더명도 인식
  • 폴더 내부에 index.js 파일이 있으면, 해당 폴더명이 라우팅 경로가 되고 index.js의 컴포넌트가 페이지로 렌더링됨

📌 Page Router 버전의 Next App 생성하기

Page Router는 이전 버전에서 주로 사용되던 방식이다.

Next.js(v15)를 설치하면 기본적으로 App Router 구조가 생성되므로, Page Router로 사용하려면 프로젝트 생성 시 옵션을 조정해야 한다.

npx create-next-app@latest my-app

설치 과정에서 TypeScript / ESLint / App Router 사용 여부 등을 선택할 수 있는데, App RouterNo로 설정하면 Page Router 구조를 사용할 수 있다.


✅ 정리

  • Page Router는 Next.js에서 제공하는 전통적인 라우팅 방식
  • pages 폴더 구조와 파일명/폴더명으로 라우팅을 관리
  • Next.js 최신 버전에서도 옵션을 통해 Page Router 프로젝트 생성 가능

2. 페이지 라우팅 설정하기

📌 기본 라우팅 구조

Next.js의 Page Routerpages 폴더 구조에 따라 URL 경로를 자동으로 매핑한다.

예를 들어 search 폴더 안에 index.tsx 파일을 작성하면 /search 경로로 접근할 수 있다.

// pages/search/index.tsx
export default function Page() {
  return <h1>Search</h1>;
}

👉 브라우저에서 http://localhost:3000/search로 접속하면 Search라는 텍스트가 표시된다.


📌 중첩 라우팅 (Nested Routing)

search 폴더 안에 setting.tsx 파일을 추가하면 /search/setting 경로가 자동으로 생성된다.

// pages/search/setting.tsx
export default function Page() {
  return <h1>search/setting</h1>;
}

👉 http://localhost:3000/search/setting 경로로 접속 시 아래와 같이 렌더링된다.

중첩 라우팅

추가로 search/setting/index.tsx 형태로 폴더 안에 index.tsx를 두면, /search/setting이 기본 경로로 인식된다. ⚠️ 이때 주의할 점은 index 파일이 항상 우선 적용된다는 점!!!


📌 쿼리스트링(Query String) 사용하기

Page Router에서는 useRouter 훅을 사용해 URL 쿼리스트링을 쉽게 처리할 수 있다.

// pages/search/index.tsx
import { useRouter } from "next/router";

export default function Page() {
  const router = useRouter();
  const { q } = router.query;

  return <h1>Search {q}</h1>;
}

👉 http://localhost:3000/search?q=이민제로 접근하면 아래처럼 표시된다.

쿼리스트링

router 객체를 출력해보면, pathname, query, asPath 등 다양한 정보를 확인할 수 있다.

router 객체


📌 동적 경로 (Dynamic Routes)

Page Router에서는 대괄호([])를 사용해 동적 경로를 정의할 수 있다.

/pages
  /index.js        // 기본 경로 (/)
  /about.js        // /about
  /posts
    ┣ index.js      // /posts
    ┗ [id].js       // /posts/1, /posts/2 ...

[id].js 파일을 만들면, /posts/1, /posts/2 와 같은 동적 경로를 처리할 수 있다.

// pages/book/[id].tsx
import { useRouter } from "next/router";

export default function Page() {
  const router = useRouter();
  const { id } = router.query;

  return <h1>Book: {id}</h1>;
}

👉 /book/1, /book/13과 같이 경로를 입력하면 각각 Book: 1, Book: 13으로 출력된다.

동적 경로


📌 Catch All Routes

만약 /book/13/14/15처럼 여러 파라미터가 들어오는 경우, 파일명을 [...id].tsx로 작성하면 배열 형태로 받을 수 있다.

// pages/book/[...id].tsx
import { useRouter } from "next/router";

export default function Page() {
  const router = useRouter();
  const { id } = router.query;

  return <h1>Book: {id}</h1>;
}

👉 http://localhost:3000/book/13/14/15로 접근 시 Book: 13,14,15 형태로 출력된다.

Catch All Routes

📌 Optional Catch All

/book 자체도 유효하게 처리하고 싶다면, 파일명을 대괄호 두 겹으로 감싼 [[...id]].tsx를 사용한다.

이 경우 세그먼트가 없으면 query.idundefined가 되어 기본 화면을 표시할 수 있다.

// pages/book/[[...id]].tsx
import { useRouter } from "next/router";

export default function Page() {
  const { query } = useRouter();
  const ids = query.id as string[] | undefined;

  // 세그먼트가 없으면 기본 안내, 있으면 값 표시
  if (!ids) return <h1>Book:</h1>;
  return <h1>Book: {ids.join(", ")}</h1>;
}

Optional Catch All

  • /book → Book: (기본 페이지 렌더)
  • /book/1 → Book: 1
  • /book/13/14/15 → Book: 13, 14, 15

📌 404 페이지 처리

존재하지 않는 경로를 처리하려면 pages/404.tsx 파일을 생성하면 된다.

// pages/404.tsx
export default function Page() {
  return <h1>존재하지 않는 페이지입니다.</h1>;
}

👉 http://localhost:3000/book/없는값 같은 잘못된 경로 접근 시 자동으로 404 페이지가 렌더링된다.

404 페이지 처리


✅ 정리

  • pages 폴더 구조를 기반으로 URL이 자동 생성된다.
  • 폴더/index.tsx = 해당 경로의 기본 페이지
  • 파일명.tsx = 파일명과 동일한 경로 생성
  • [id].tsx = 동적 파라미터 라우팅
  • [...id].tsx = 여러 파라미터 처리 (Catch All Routes)
  • [[...id]].tsx = 세그먼트 0개도 허용(옵셔널)
  • 404.tsx = 에러 페이지 처리

3. 네비게이팅 (Navigating)

Next.js에서 페이지 간 이동을 구현할 때 단순히 <a> 태그를 사용할 수도 있지만, 이는 CSR 방식과 충돌하며 불필요한 리렌더링을 발생시킬 수 있다.

그래서 Next.js는 자체적으로 최적화된 Link 컴포넌트를 제공하며, 사용법은 <a>와 거의 동일하다.

// pages/_app.tsx
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import Link from 'next/link';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <header>
        <Link href="/">Index</Link>
        &nbsp;
        <Link href="/search">Search</Link>
        &nbsp;
        <Link href="/book/1">Book/1</Link>
      </header>
      <Component {...pageProps} />
    </>
  );
}

Link 컴포넌트

👉 위처럼 Link 컴포넌트를 사용하면, 클라이언트 사이드 라우팅 방식으로 부드럽게 페이지 전환이 이루어진다.


📌 프로그래매틱 네비게이션 (Programmatic Navigation)

경우에 따라서는 단순히 <Link>로 이동하지 않고, 버튼 클릭이나 특정 로직에 따라 프로그래매틱하게 페이지를 이동해야 할 수도 있다. 이때는 useRouter 훅을 활용!

// pages/_app.tsx
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import Link from 'next/link';
import { useRouter } from 'next/router';

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();

  const onClickButton = () => {
    router.push('/test'); // 원하는 경로로 이동
  };

  return (
    <>
      <header>
        <Link href="/">Index</Link>
        &nbsp;
        <Link href="/search">Search</Link>
        &nbsp;
        <Link href="/book/1">Book/1</Link>
        &nbsp;
        <button onClick={onClickButton}>/test 페이지 이동</button>
      </header>
      <Component {...pageProps} />
    </>
  );
}

프로그래매틱 네비게이션

  • router.push('/path') → 새로운 경로로 이동
  • router.replace('/path') → 현재 기록을 덮어쓰기 (뒤로가기 불가)
  • router.back() → 이전 페이지로 이동

✅ 정리

  • 단순 네비게이션: <Link href="..."> 컴포넌트를 사용
  • 조건/이벤트 기반 네비게이션: useRouter().push() replace(), back() 등을 활용
  • 모두 CSR 최적화 방식으로 동작하기 때문에, 매끄럽고 빠른 페이지 전환이 가능하다는 점!

4. 프리패칭(Pre-Fetching)

Next.js Page Router에서는 사용자가 현재 보고 있는 페이지에서 이동할 가능성이 있는 다른 페이지를 미리 불러오는 기능을 제공하며, 이를 프리패칭(Pre-Fetching)이라고 한다.

📌 프리패칭의 동작 원리

프리패칭 개념도

  • 사용자가 현재 보고 있는 페이지에서 이동 가능한 링크들을 미리 수집한다.
  • 그리고 해당 페이지에서 필요한 JS Bundle을 사전에 다운로드한다.
  • 덕분에 페이지 전환 시 추가적인 요청 없이 즉시 렌더링이 가능하다.

📌 왜 프리패칭이 필요한가?

프리패칭 처리 과정

초기 렌더링 시 모든 자바스크립트 코드를 한 번에 내려주는 것이 아니라, 페이지 단위로 필요한 JS Bundle만 전달된다.
이렇게 하면 서버에서 불필요한 자바스크립트 양을 줄일 수 있고, Hydration 속도도 빨라진다.

⚠️ 하지만 문제는 이후 Client Side Rendering(CSR) 방식으로 이동할 때 발생한다.

  • 만약 프리패칭이 없다면, 새로운 페이지로 이동할 때마다 JS Bundle을 네트워크 요청으로 가져와야 한다.
  • 반면 프리패칭이 적용되면, 사용자가 이동하기 전에 이미 JS Bundle을 내려받아 두기 때문에 즉각적인 페이지 이동이 가능하다.

📌 Pre-Fetching의 동작 과정

  • 초기 접속이 완료되면, Next.js가 자동으로 프리패칭을 수행한다.
  • 사용자가 이동할 가능성이 있는 페이지의 JS Bundle을 미리 불러온다.
  • 이후 실제로 해당 링크를 클릭하면, 추가 요청 없이 바로 페이지가 교체된다.
import { useEffect } from "react";
import { useRouter } from "next/router";
import Link from "next/link";

export default function App() {
  const router = useRouter();

  const onClickButton = () => {
    router.push("/test");
  };

  useEffect(() => {
    router.prefetch("/test"); // 사전 로드
  }, []);

  return (
    <header>
      <Link href="/">Index</Link>&nbsp;
      <Link href="/search">Search</Link>&nbsp;
      <Link href="/book/1">Book/1</Link>
      <div>
        <button onClick={onClickButton}>/test 페이지 이동</button>
      </div>
    </header>
  );
}

📌 프리패칭을 끄고 싶을 때

Next.js의 Link 컴포넌트는 기본적으로 프리패칭이 활성화되어 있는데, 경우에 따라 프리패칭을 막고 싶다면 prefetch={false} 옵션을 사용하면 된다.

import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import Link from 'next/link';
import { useRouter } from 'next/router';

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();
  const onClickButton = () => {
    router.push('/test');
  };
  
  
  useEffect(() => {
    router.prefetch('/test');
  }, []);

  return (
    <>
      <header>
        <Link href={'/'}>Index</Link>
        &nbsp;
        <Link href={'search'}>Search</Link>
        &nbsp;
        <Link href={'/book/1'}>Book/1</Link>
        <div>
          <button onClick={onClickButton}>/test 페이지 이동</button>
        </div>
      </header>
      <Component {...pageProps} />
    </>
  );
}

프리패칭


✅ 정리

  • 프리패칭은 사용자가 이동하기 전에 페이지를 미리 불러오는 기능이다.
  • 덕분에 페이지 전환 시 빠른 응답성과 부드러운 사용자 경험을 제공한다.
  • Link 컴포넌트는 기본적으로 프리패칭을 수행하며, 필요할 경우 prefetch={false}로 끌 수 있다.
  • router.prefetch()를 이용하면 특정 경로를 직접 프리패칭할 수도 있다.
profile
큐트걸

0개의 댓글