dev.syw

폴더로 정의하는 라우트와 동적·중첩·그룹 경로.

App Router 라우팅

App Router에서 라우팅은 코드가 아니라 폴더 구조로 결정됩니다. 폴더를 만들면 경로가 생기고, 그 안의 page.tsx 가 화면이 됩니다. 이번 레슨에서는 정적 경로부터 동적·중첩·그룹 경로, 그리고 페이지 사이를 이동하는 방법까지 살펴봅니다.

학습 목표

  • 폴더 = 라우트라는 규칙을 이해한다.
  • 동적 세그먼트 [slug] 로 가변 경로를 만든다.
  • 중첩 라우트와 라우트 그룹 (folder) 을 구분한다.
  • Link, useRouter, params 로 이동하고 값을 읽는다.

폴더가 곧 라우트

폴더 이름이 URL이 되고, 그 안의 page.tsx 가 페이지가 됩니다.

app/
├── page.tsx                →  /
├── dashboard/
│   ├── page.tsx            →  /dashboard
│   └── settings/page.tsx   →  /dashboard/settings
└── shop/
    └── cart/page.tsx       →  /shop/cart

page.tsx 가 없는 폴더는 URL을 만들지 않습니다. 폴더는 구조를 나누는 용도로만 쓸 수도 있습니다.

동적 세그먼트 [slug]

대괄호로 폴더 이름을 감싸면 가변 경로가 됩니다. /blog/hello, /blog/world 처럼 어떤 값이 와도 같은 페이지가 처리합니다.

app/
└── blog/
    └── [slug]/page.tsx     →  /blog/무엇이든

페이지는 params 를 통해 그 값을 받습니다. App Router 최신 버전에서 paramsPromise 이므로 await 합니다.

// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <h1>글: {slug}</h1>;
}

여러 세그먼트를 한꺼번에 받고 싶으면 [...slug](catch-all)를 씁니다. /docs/a/b/c['a', 'b', 'c'] 로 들어옵니다.

중첩과 라우트 그룹

폴더 안에 폴더를 넣으면 경로가 그대로 중첩됩니다. 반면 소괄호로 감싼 폴더 (group) 는 URL에 나타나지 않습니다. 코드를 묶어 정리하거나, 레이아웃을 다르게 적용할 때 유용합니다.

app/
├── (marketing)/
│   ├── layout.tsx          →  마케팅 전용 레이아웃
│   └── about/page.tsx      →  /about   ( (marketing) 은 URL에 없음)
└── (shop)/
    ├── layout.tsx          →  쇼핑 전용 레이아웃
    └── products/page.tsx   →  /products

💡 TIP — 같은 URL 깊이지만 레이아웃을 나누고 싶을 때 라우트 그룹이 빛납니다. 예를 들어 로그인 화면과 본 화면의 레이아웃을 분리할 수 있습니다.

병렬·인터셉트 라우트 (맛보기)

조금 더 고급 기능도 있습니다. 지금은 "이런 게 있다" 정도만 알아 두면 됩니다.

표기이름쓰임
@folder병렬 라우트한 레이아웃에 여러 슬롯을 동시에 렌더
(.)folder인터셉트 라우트모달처럼 다른 경로를 가로채 겹쳐 표시

페이지 이동하기

화면 간 이동은 <a> 대신 next/linkLink 를 씁니다. 페이지 전체를 새로고침하지 않고 부드럽게 전환됩니다.

import Link from 'next/link';

export default function Nav() {
  return (
    <nav>
      <Link href="/"></Link>
      <Link href="/blog/hello">첫 글</Link>
    </nav>
  );
}

코드 안에서 이동하거나 검색 파라미터를 읽으려면 클라이언트 컴포넌트에서 훅을 씁니다.

'use client';
import { useRouter, useSearchParams } from 'next/navigation';

export default function SearchButton() {
  const router = useRouter();
  const params = useSearchParams();

  return (
    <button onClick={() => router.push(`/search?q=${params.get('q') ?? ''}`)}>
      검색
    </button>
  );
}

⚠️ 주의useRouternext/navigation 에서 가져옵니다. 예전 Pages Router의 next/router 와 헷갈리지 마세요.

요약

  • 폴더 구조가 그대로 URL이 되고, page.tsx 가 화면이 된다.
  • [slug] 는 동적 세그먼트이며 params(Promise)로 값을 받는다.
  • (group) 은 URL에 드러나지 않고 코드/레이아웃을 묶는다.
  • 이동은 Link, 코드 내 이동·파라미터 읽기는 useRouter/useSearchParams 로 한다.

연습문제

  1. /products/[id] 페이지를 만들어 id 를 화면에 출력해 보세요.
  2. 라우트 그룹 (auth) 를 만들고 그 안에 /login, /signup 페이지를 두세요. URL에 auth 가 나타나지 않는지 확인하세요.
  3. 네비게이션 바를 만들고 Link 로 세 페이지를 연결해 보세요.
  4. catch-all 라우트 [...path] 가 어떤 경우에 유용할지 한 가지 예를 적어 보세요.

힌트 — 동적 페이지에서 const { id } = await params; 로 값을 꺼냅니다. 라우트 그룹 폴더 이름은 반드시 소괄호로 감싸야 합니다.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

Next.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.