라우팅(Routing)
2025-12-08
라우팅(routing)은 웹사이트의 여러 페이지 사이를 이동하는 것으로 각각의 경로를 라우트(route)라고 부르며, 웹사이트에서 링크를 누르면 다른 페이지로 이동하는 과정을 의미합니다.
Next.js 라우팅은 폴더 구조 기반으로 라우트를 구성하며, 각 라우트가 트리(tree) 형태로 구성되어 있습니다. 그리고 이렇게 트리에 존재하는 각 노드(node)들은 URL 경로의 각 세그먼트에 매핑됩니다.
https://www.example.com/dashboard/analytics
dashboard/analytics)이 되며, 이는 세그먼트들의 조합/)로 구분(dashboard, analytics)되며 계층구조로 형성app 폴더 기반으로 리액트 (서버) 컴포넌트 기반으로 구성Remark. 아래 폴더 구조를 참고해서 URL을 예측하고, URL을 보고 폴더 구조를 작성할 수 있어야 함
| 파일 이름 | 설명 | 역할 |
|---|---|---|
layout |
공통 UI | 레이아웃 |
page |
개별 UI | 페이지 |
loading |
로딩 UI | 로딩 화면 |
not-found |
404 UI | 404 페이지 |
error |
전역 오류 UI | 에러 화면 |
route |
서버 API 엔드포인트 | API |
template |
재사용 가능한 UI | 템플릿 |
default |
라우트의 대체 UI | 대체 UI |
app/layout.tsx 파일에 레이아웃 컴포넌트를 적용하면 모든 페이지에 적용웹 개발에서는 /pokemon/1, /pokemon/2와 같이 동적으로 바뀌는 경로(파라미터)가 흔히 필요합니다. Next.js는 이러한 동적 라우트를 Dynamic Segment(동적 세그먼트)라는 개념으로 쉽게 처리할 수 있게 해줍니다.
동적 세그먼트는 폴더명이나 파일명을 [param] (대괄호)로 지정합니다. 대괄호 안의 이름은 곧 파라미터 이름이자 props로 전달되는 키(key) 이름입니다.
실제로 /pokemon/1에 접속하면 { pokemonId: '1' } 이 props로 들어오고, /pokemon/2로 접속하면 { pokemonId: '2' }가 들어오게 됩니다.
[pokemonId]/page.tsx \(\to\) /pokemon/1 \(\to\) { pokemonId: '1' }[pokemonId]/page.tsx \(\to\) /pokemon/2 \(\to\) { pokemonId: '2' }(만약) 동적 라우트가 한정된 값만 가질 경우(블로그 글 1~10개 등)에는 generateStaticParams 함수를 활용해, 빌드 시점에 모든 정적 경로를 미리 만들어둘 수 있습니다. 아래와 같은 코드는 포켓몬 상세 페이지가 미리 정적으로 생성되어, 데이터베이스에 따로 접속하지 않고도 빠르게 화면을 보여줄 수 있습니다.
동적 세그먼트는 [...param]처럼 대괄호 안에 ...(점 세 개)를 붙이면, 해당 부분 뒤의 모든 세그먼트를 배열로 받아 처리할 수 있습니다.
[...info]/page.tsx \(\to\) /pokemon/1 \(\to\) { info: ['1'] }[...info]/page.tsx \(\to\) /pokemon/1/overview \(\to\) { info: ['1', 'overview'] }[...info]/page.tsx \(\to\) /pokemon/1/overview/detail \(\to\) { info: ['1','overview','detail'] }이중 대괄호([[...param]])를 쓰면, 해당 파라미터가 없어도(=생략되어도) 매칭됩니다. 아래는 이중 대괄호를 사용한 예시입니다.
[[...info]]/page.tsx \(\to\) /pokemon \(\to\) {}[[...info]]/page.tsx \(\to\) /pokemon/1 \(\to\) { info: ['1'] }[[...info]]/page.tsx \(\to\) /pokemon/1/overview \(\to\) { info: ['1', 'overview'] }[[...info]]/page.tsx \(\to\) /pokemon/1/overview/detail \(\to\) { info: ['1','overview','detail'] }타입스크립트라면, 아래처럼 각 라우트 세그먼트별로 params의 타입을 명확히 정의해두면 좋습니다.
[pokemonId]/page.tsx \(\to\) { pokemonId: string }[...info]/page.tsx \(\to\) { info: string[] }[[...info]]/page.tsx \(\to\) { info?: string[] }[type]/[pokemonId]/page.tsx \(\to\) { type: string; pokemonId: string }여러 값을 다이나믹하게 받으려면 [...info]를 활용합니다.
// app/pokemon/[...info]/page.tsx
interface PageProps {
params: {
info: string[];
};
}
export default function PokemonInfoPage({ params }: PageProps) {
// params.info: 경로의 세그먼트들이 배열로 들어옴
return (
<div>
<h1>포켓몬 정보</h1>
<ul>
{params.info.map((segment, idx) => (
<li key={idx}>{segment}</li>
))}
</ul>
</div>
);
}// app/pokemon/[[...info]]/page.tsx
interface PageProps {
params: {
info?: string[];
};
}
export default function PokemonOptionalInfoPage({ params }: PageProps) {
return (
<div>
<h1>포켓몬 선택 정보</h1>
{params.info ? (
<ul>
{params.info.map((segment, idx) => (
<li key={idx}>{segment}</li>
))}
</ul>
) : (
<p>파라미터가 없습니다.</p>
)}
</div>
);
}<Link>Next.js의 <Link> 컴포넌트는 HTML <a>를 확장해, 자동 프리페치(prefetch)와 클라이언트 내비게이션이 지원되는 내장 라우팅 컴포넌트입니다. 가장 기본적인 사용법은 아래와 같습니다.
<Link> 권장라우트 이동이 필요하다면 다음과 같이 useRouter 훅을 사용할 수 있습니다. 하지만 라우터로 이동시킬 경우, 웹사이트에서 버튼/링크 위에 마우스를 올려도 목적지 경로가 보이지 않고, 새 탭 열기(macOS command+클릭 등)도 불가능합니다. 따라서, 대부분의 경우 <Link> 컴포넌트를 사용하는 것이 권장됩니다.
<Link> 예제import Link from 'next/link';
export default function LinkExample() {
return (
<div>
<h2>Next.js Link 예제</h2>
<ul>
<li>
<Link href="/about">About 페이지로 이동</Link>
</li>
<li>
<Link href="/pokemon">
<button type="button">
포켓몬 목록으로 이동 (버튼 모양)
</button>
</Link>
</li>
<li>
<Link href="https://nextjs.org" target="_blank" rel="noopener noreferrer">
공식 Next.js 사이트 (새 탭)
</Link>
</li>
<li>
<Link href="/docs" prefetch={false}>
프리페치 해제 예시
</Link>
</li>
</ul>
</div>
);
}
// prefetch 옵션은 페이지를 프리페치(prefetch)할지 여부를 결정합니다. 프리페치는 페이지를 미리 로드하는 것을 의미합니다.redirect서버 컴포넌트에서 조건에 따라 다른 경로로 리디렉션(redirect)하고 싶을 때는 redirect 함수를 사용하면 됩니다.
redirect 사용시 주의 사항redirect 함수 정보는 다음과 같습니다.
redirect 제한 사항useRouter 훅을 사용)redirect는 절대경로(URL)도 지원하므로 외부 페이지로 이동할 수도 있음