
🌱 해당 포스트는 한 입 크기로 잘라먹는 Next.js(v15)을 수강하고, Next.js 공식 문서 - page router를 참고하여 정리한 글입니다.
2025년 기준으로 Next.js는 두 가지 라우팅 방식을 제공한다. App router와 Page Router이다. Page Router가 먼저 등장한 라우터로, 현재 많은 기업에서 사용되고 있는 안정적인 라우터이다.
🚏 Routing (라우팅) 이란?
어떤 URL 요청이 왔을 때, 어떤 페이지(컴포넌트)를 보여줄지를 결정하는 규칙
Page Router 는 파일 시스템 기반으로 한 라우터를 제공한다. pages directory 안에 파일이 추가되면, 자동적으로 라우트가 된다.

pages 폴더 아래에 있는 index.tsx 파일에 있는 컴포넌트가 /(루트 라우트)의 페이지로 렌더링된다.

이름.tsx 파일을 만들면, /이름 경로일 때 해당 컴포넌트를 페이지로 렌더링한다.

원하는 경로 이름으로 폴더를 만들고, 그 안에 index.tsx 파일을 만들면, 폴더 이름으로 경로가 만들어지고, 해당 경로로 접속했을 때, index.tsx 파일을 페이지로 렌더링한다.
동적 경로(Dynamic Routes) 란 경로에 가변적인 값을 포함하고 있는 경우를 말한다. 여기서 가변적인 값들을 url 파라미터라고 부른다.
아래와 같이 URL의 일부가 변수처럼 바뀔 수 있는 구조를 말한다.
/post/1
/post/2
/post/3
/pages
└── blog
└── [id].tsx
[id].tsx는 id라는 변수에 따라 다른 콘텐츠를 보여줌
→ 마치 postId를 파라미터로 받는 느낌
/blog/1
/blog/hello-world
/blog/42
// pages/blog/[id].tsx
import { useRouter } from 'next/router'
export default function BlogPost() {
const router = useRouter();
const { id } = router.query;
return <h1>블로그 글 ID: {id}</h1>;
}
const { id } = router.query; 에서 router의 query 속성을 통해 값을 가져올 수 있다.
⚠️ 단, /blog/id 경로에만 대응된다. 따라서 /blog/123은 대응되지만, /blog/123/123은 대응되지 않는다.
[]만 사용하게 되면, /blog/123은 대응되지만, /blog/123/123은 대응되지 않는다. /blog/123/123와 같은 경로도 대응할 수 있는 범용적인 페이지를 만들고 싶다면, [id].tsx가 아닌 [...id].tsx로 만들어주면 된다.
/pages
└── blog
└── [...id].tsx
| Route | Example URL | params |
|---|---|---|
| pages/blog/[...id].tsx | /blog/123 | { id: ['123'] } |
| pages/blog/[...id].tsx | /blog/123/456 | { id: ['123', '456'] } |
| pages/blog/[...id].tsx | /blog/123/456/789 | { id: ['123', '456', '789'] } |
// pages/blog/[...id].tsx
import { useRouter } from 'next/router'
export default function BlogPost() {
const router = useRouter();
const { id } = router.query;
console.log(id); // '/blog/123/456'의 경우 ["123", "456"] 이렇게 배열로 출력
return <h1>블로그 글</h1>;
}
배열 값으로 모든 url 파라미터 값이 나온다.
⚠️ 단, /blog와 같이 url 파라미터가 아예 없는 경우에는 대응되지 않는다.
catch all segment는 /blog 뒤에 무슨 경로라도 나와야 매칭이 된다.
url 파라미터가 아예 없는 경우, catch all segment로 하면, index 경로에는 대응되지 않는다.
즉, /blog 페이지에는 대응되지 않는다는 의미이다.
만약에 정말 더 범용적으로 /blog 뒤에 어떤 경로가 나오든 다 대응하도록 하고 싶다면, 대괄호로 한번 더 감싸자!
/pages
└── blog
└── [[...id]].tsx
| Route | Example URL | params |
|---|---|---|
| pages/blog/[[...id]].tsx | /blog/ | { id: undefined } |
| pages/blog/[[...id]].tsx | /blog/123/ | { id: ['123'] } |
| pages/blog/[[...id]].tsx | /blog/123/456 | { id: ['123', '456'] } |
특정 url에 대응되는 페이지가 없을 때 보여주기 위한 404 페이지는 아래와 같이 만들 수 있다.
/pages
└── 404.tsx
정적 경로 :
경로.tsx혹은 경로 폴더 아래index.tsx
동적 경로 :[id].tsx
범용적 경로 :[...id].tsx,[[...id]].tsx
없는 경로 (404 페이지) :404.tsx