라우팅 완전 정복
Next.js의 라우팅은 파일 시스템 기반으로 동작합니다. 이 글에서는 App Router와 Page Router의 라우팅 시스템을 모두 다룹니다.
Page Router 기초
섹션 제목: “Page Router 기초”Page Router는 pages 폴더를 기반으로 합니다.
기본 라우팅
섹션 제목: “기본 라우팅”pages/index.tsx가 메인 페이지- 파일이 곧 하나의 페이지, 파일 이름이 경로
폴더/index.tsx→baseUrl/폴더이름폴더/list.tsx→baseUrl/폴더이름/list
Dynamic Routing
섹션 제목: “Dynamic Routing”[id].tsx로 파일을 만들면 동적 경로 생성useRouter()로 쿼리 파라미터 접근
import { useRouter } from "next/router";
function SomeComponent() { const router = useRouter(); console.log(router.query); // { id: "something" } console.log(router.pathname); // 현재 경로}Catch-All 라우트
섹션 제목: “Catch-All 라우트”[...slug].tsx로 파일 이름 설정/폴더이름/id/2/guest→{ slug: ["id", "2", "guest"] }
Link 컴포넌트
섹션 제목: “Link 컴포넌트”import Link from "next/link";
<Link href={{ pathname: "clients/[id]", query: { id: something }, }}> 이동</Link><a>와 같은 기능이지만 페이지 새로고침이 발생하지 않습니다.
App Router 라우팅
섹션 제목: “App Router 라우팅”Route Group
섹션 제목: “Route Group”(폴더명)형태로 작성하면 URL 경로에 포함되지 않음- 각각의
layout.tsx페이지를 생성 가능 - 목적에 따라 파일 분류에도 활용
Dynamic Route
섹션 제목: “Dynamic Route”post/[id]→post/1,post/2가능,post/1/123불가능post/[[...id]](Catch-All) →post/1/123,post/2/22/44가능
병렬 라우트 (Parallel Routes)
섹션 제목: “병렬 라우트 (Parallel Routes)”별도의 경로를 가지는 2개의 페이지를 한 페이지에서 동시에 렌더링하는 것입니다. 폴더 경로는 @로 시작합니다.
- archive - layout.js ├── @archive - page.js └── @latest - page.jsexport default function ArchiveLayout({ archive, latest }) { return ( <div> <section>{archive}</section> <section>{latest}</section> </div> );}@ 뒤에 적힌 경로를 키로 가진 프로퍼티를 layout의 props에서 사용할 수 있습니다.
병렬 라우트에서의 중첩 라우트
섹션 제목: “병렬 라우트에서의 중첩 라우트”default.js를 사용해야 합니다. archive 페이지에서 중첩 라우트인 /archive/someUrl로 이동하면 latest에서 보여줄 내용이 없어집니다. 이때 default.js를 통해 기본값을 설정합니다.
// @latest/default.jsexport default function LatestDefault() { return null;}인터셉팅 라우트 (Intercepting Routes)
섹션 제목: “인터셉팅 라우트 (Intercepting Routes)”(.)intercept할URL: 같은 디렉토리(..)intercept할URL: 상위 디렉토리- 새로고침이나 경로 직접 입력 시 해당 URL로 이동
- Link 태그 등으로 접속 시 인터셉팅 페이지 실행
모달 구현: 인터셉트 + 병렬 라우팅
섹션 제목: “모달 구현: 인터셉트 + 병렬 라우팅”서버 컴포넌트에서 모달을 구현할 때 사용합니다. 병렬 라우터는 폴더를 무시하므로 경로는 (.)으로 설정합니다.
@modal → (.)image ✅@modal → (..)image ❌ (병렬 라우터가 폴더를 무시하므로)Route Handler
섹션 제목: “Route Handler”Next.js 13 이후의 API 처리 방식입니다. HTTP 메서드별로 함수를 분리할 수 있습니다.
export function GET(request) { return new Response("Hello!");}
export function POST(request) { // POST 처리}api폴더 내에route.ts생성 (같은 경로에page.tsx가 있을 수 없음)- Next.js 애플리케이션을 모바일 앱의 API 서버로 활용할 때 유용
Route Handler 성능 최적화
섹션 제목: “Route Handler 성능 최적화”독립적인 비동기 작업은 병렬로 실행합니다:
// Anti-pattern: 순차 실행export async function GET(request) { const session = await auth(); const config = await fetchConfig(); const data = await fetchData(session.user.id); return Response.json({ data, config });}
// 올바른 패턴: 병렬 실행export async function GET(request) { const sessionPromise = auth(); const configPromise = fetchConfig(); const session = await sessionPromise; const [config, data] = await Promise.all([ configPromise, fetchData(session.user.id), ]); return Response.json({ data, config });}useRouter
섹션 제목: “useRouter”"use client"에서만 사용 가능router.back()으로 뒤로가기 실행
"use client";import { useRouter } from "next/navigation";
function Component() { const router = useRouter(); return <button onClick={() => router.back()}>뒤로가기</button>;}Middleware
섹션 제목: “Middleware”루트 경로에 middleware.ts를 생성하여 모든 요청을 검토할 수 있습니다. 인증 구현 시 자주 사용됩니다.
import { NextResponse } from "next/server";
export function middleware(request) { // 인증 확인 등의 로직 return NextResponse.next();}
// 특정 경로에만 적용export const config = { matcher: "/news",};Page Router: Pre-fetching
섹션 제목: “Page Router: Pre-fetching”getStaticProps
섹션 제목: “getStaticProps”빌드 타임에 한 번 실행되어 페이지를 사전 렌더링합니다.
export async function getStaticProps() { return { props: { products: [{ id: "p1", title: "Product 1" }], }, };}
function Home(props) { // props.products 사용}ISR (Incremental Static Regeneration)
섹션 제목: “ISR (Incremental Static Regeneration)”revalidate 프로퍼티를 추가하면 설정한 주기마다 페이지를 재생성합니다.
export async function getStaticProps() { return { props: { products: [{ id: "p1", title: "Product 1" }] }, revalidate: 10, // 10초마다 재생성 };}getStaticPaths
섹션 제목: “getStaticPaths”동적 라우트에서 사전 생성할 경로를 지정합니다.
export async function getStaticPaths() { return { paths: [ { params: { productId: "p1" } }, { params: { productId: "p2" } }, ], fallback: false, };}fallback 옵션:
false: 지정하지 않은 경로는 404true: 미생성 경로에 fallback 버전 제공"blocking": HTML 생성까지 기다린 후 캐시
getServerSideProps
섹션 제목: “getServerSideProps”매 요청마다 서버에서 실행되어 새로운 데이터로 페이지를 생성합니다. getStaticPaths가 필요 없습니다.
export const getServerSideProps = (context) => { const { params, req, res } = context; return { props: { userName: "Kim" }, };};| 기능 | Page Router | App Router |
|---|---|---|
| 기본 라우팅 | 파일 기반 | 폴더 기반 |
| 동적 라우팅 | [id].tsx | [id]/page.tsx |
| API | pages/api/handler.ts | app/api/route.ts |
| 사전 렌더링 | getStaticProps / getServerSideProps | 서버 컴포넌트 |
다음 글에서는 Next.js의 캐싱 전략을 살펴봅니다.