다다의 개발일지 6v6

[Next.js] App Router - 디렉토리 기반의 라우팅! 신기하다.. 본문

Frontend/Next.js

[Next.js] App Router - 디렉토리 기반의 라우팅! 신기하다..

dev6v6 2025. 3. 1. 16:49

앱 라우팅(App Router) 

Next.js 13부터 디렉토리 기반의 라우팅 App Router가 도입됐다고 한다.

기존의 pages/ 방식과 다르게, 파일과 폴더 구조만으로 자동으로 라우팅이 설정된다

 

 

기본 구조

Next.js에서 app/ 폴더 안에 파일을 추가하는 것만으로 페이지를 생성할 수 있음

📂 디렉토리 구조 예시

/app
  ├─ layout.tsx       # 모든 페이지에 공통 적용 (레이아웃)
  ├─ page.tsx         # `/` (홈 페이지)
  ├─ about/
  │   ├─ page.tsx     # `/about` 페이지
  ├─ blog/
  │   ├─ page.tsx     # `/blog` 페이지
  │   ├─ post/
  │   │   ├─ page.tsx # `/blog/post` 페이지
  • page.tsx → 해당 경로의 메인 페이지
  • layout.tsx → 해당 폴더 내 모든 페이지에 공통 적용되는 레이아웃

 

동적 라우팅 (Dynamic Routing)

폴더 이름을 [param] 형태로 만들면, 동적인 경로를 처리할 수 있다

 

예제: 동적인 블로그 포스트 페이지

/app
  ├─ blog/
  │   ├─ [id]/
  │   │   ├─ page.tsx  # `/blog/:id` 형태의 동적 페이지

 

/blog/hello-world 요청 시 id 값 가져오기

// app/blog/[id]/page.tsx
import { useParams } from "next/navigation";

export default function BlogPost() {
  const params = useParams(); // { id: "hello-world" }
  
  return <h1>Blog Post: {params.id}</h1>;
}
  • /blog/hello-world → params.id = "hello-world"
  • /blog/my-first-post → params.id = "my-first-post"

 

병렬 라우팅 (Parallel Routes)

Next.js에서는 여러 레이아웃을 동시에 렌더링할 수도 있다

 

예제: 대시보드의 사이드바 & 콘텐츠 분리

/app
  ├─ layout.tsx
  ├─ dashboard/
  │   ├─ layout.tsx  # 대시보드 전체 레이아웃
  │   ├─ page.tsx    # `/dashboard` 메인 페이지
  │   ├─ sidebar.tsx # 사이드바

 

layout.tsx에서 병렬 렌더링 적용

// app/dashboard/layout.tsx
export default function DashboardLayout({ children, sidebar }: { children: React.ReactNode, sidebar: React.ReactNode }) {
  return (
    <div style={{ display: "flex" }}>
      <aside>{sidebar}</aside>
      <main>{children}</main>
    </div>
  );
}

 

 

 

인터셉트 라우팅 (Intercepting Routes)

  • 기존 페이지를 변경하지 않고, 특정 조건에서 기존 페이지 대신 새로운 UI를 띄우는 기능
  • 예를 들어, 모달을 띄우고 싶을 때 사용 가능!
  • /profile을 직접 방문하면 원래 페이지가 뜨고, 특정 상황에서만 모달을 띄움.
  • 어떤 페이지에서 이동하느냐에 따라 다르게 렌더링되게 만드는 기능

예제: /profile 대신 모달 띄우기

/app
  ├─ profile/
  │   ├─ page.tsx    # 기본 `/profile` 페이지
  ├─ (modal)/
  │   ├─ profile/
  │   │   ├─ page.tsx # `/profile`를 모달로 인터셉트
// app/(modal)/profile/page.tsx
export default function ProfileModal() {
  return <div className="modal">✨ 모달 프로필 화면 ✨</div>;
}

 

적용하는 방법 (링크 설정)

Next.js에서 <Link>를 사용할 때, as 속성을 이용하면 인터셉트 라우팅을 적용할 수 있다

import Link from "next/link";

export default function Dashboard() {
  return (
    <div>
      <h1>대시보드</h1>
      {/* `as` 속성을 추가하면 인터셉트 라우팅 적용됨 */}
      <Link href="/profile" as="/dashboard?modal=profile">
        프로필 보기 (모달)
      </Link>
    </div>
  );
}
  • 모달을 띄울 때 → /dashboard?modal=profile
  • 일반 방문 → /profile 그대로 실행

App Router에서 모달을 띄우는 방법 (3가지)

더보기

as는 단순히 URL을 다르게 보이게 하기 위한 선택적인 기능이고, 모달을 띄우는 방법 자체는 여러 가지가 있다!

  1. as 속성을 활용한 인터셉트 라우팅 → useParams()로 상태 변경
  2. URL 상태 기반 모달 (useSearchParams()) → ?modal=true 같은 쿼리 파라미터 사용
  3. 전역 상태 관리 (Recoil, Zustand 등) → URL과 관계없이 모달 상태 관리

1️⃣ as 속성을 활용한 인터셉트 라우팅

인터셉트 라우팅을 활용하면, /profile을 직접 입력하면 기본 페이지가 뜨고, 특정 상황에서만 모달이 뜨도록 할 수 있다

📂 디렉토리 구조

/app
  ├─ profile/
  │   ├─ page.tsx    # 기본 `/profile` 페이지
  ├─ (modal)/profile/
  │   ├─ page.tsx    # `/profile`을 인터셉트해서 모달로 띄우는 페이지

대시보드에서 프로필을 모달로 띄우기

import Link from "next/link";

export default function Dashboard() {
  return (
    <div>
      <h1>대시보드</h1>
      {/* `as` 속성을 추가하면 인터셉트 라우팅 적용됨 */}
      <Link href="/profile" as="/dashboard?modal=profile">
        프로필 보기 (모달)
      </Link>
    </div>
  );
}

 

  • /profile을 직접 방문하면 app/profile/page.tsx가 실행됨
  • /dashboard?modal=profile로 이동하면 app/(modal)/profile/page.tsx가 실행되면서 모달이 뜸

 

2️⃣ useSearchParams()로 URL 상태 기반 모달 띄우기

인터셉트 라우팅 없이도 useSearchParams()를 활용해서 쿼리 파라미터를 감지하면 모달을 띄울 수 있다!

대시보드에서 쿼리 기반으로 모달 띄우기

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

export default function Dashboard() {
  const searchParams = useSearchParams();
  const router = useRouter();
  
  const isModalOpen = searchParams.get("modal") === "profile";

  return (
    <div>
      <h1>대시보드</h1>
      {/* 클릭하면 ?modal=profile 추가됨 */}
      <button onClick={() => router.push("?modal=profile")}>
        프로필 보기 (모달)
      </button>

      {/* 모달이 열릴 때만 표시 */}
      {isModalOpen && (
        <div className="modal">
          <h2>프로필 모달</h2>
          <button onClick={() => router.push("/")}>닫기</button>
        </div>
      )}
    </div>
  );
}

 

  • ?modal=profile이 URL에 추가되면 모달이 열림
  • 닫기 버튼을 누르면 router.push("/")로 변경되면서 모달 닫힘

→ URL이 상태 관리 역할을 하도록 하는 방법

 

3️⃣ 전역 상태 관리 라이브러리 활용 (Recoil, Zustand 등)

Next.js의 앱 라우팅을 이용하지 않고도 상태 관리 라이브러리(Recoil, Zustand)를 활용해서 모달을 띄울 수도 있음

// store/modalStore.ts
import { create } from "zustand";

type ModalStore = {
  isOpen: boolean;
  openModal: () => void;
  closeModal: () => void;
};

export const useModalStore = create<ModalStore>((set) => ({
  isOpen: false,
  openModal: () => set({ isOpen: true }),
  closeModal: () => set({ isOpen: false }),
}));
"use client";
import { useModalStore } from "@/store/modalStore";

export default function Dashboard() {
  const { isOpen, openModal, closeModal } = useModalStore();

  return (
    <div>
      <h1>대시보드</h1>
      <button onClick={openModal}>프로필 보기 (모달)</button>

      {isOpen && (
        <div className="modal">
          <h2>프로필 모달</h2>
          <button onClick={closeModal}>닫기</button>
        </div>
      )}
    </div>
  );
}
  • openModal() 호출하면 isOpen이 true로 변경되면서 모달 열림
  • closeModal() 호출하면 isOpen이 false가 되어 모달 닫힘

→ URL과 무관하게 모달을 띄울 수 있는 방법

어떤 방법을 써야 할까?

as를 활용한 인터셉트 라우팅 페이지 이동 기반, 자동으로 상태 관리 Next.js 라우팅을 이해해야 함 SSR & SEO가 중요한 경우
useSearchParams() 활용 간단하게 URL 상태 관리 가능 URL이 바뀌는 게 싫을 수도 있음 클라이언트 사이드에서 모달 띄울 때
Zustand 같은 전역 상태 관리 URL 영향 없이 자유롭게 모달 띄울 수 있음 새로고침하면 상태 초기화됨 URL과 관계없이 모달이 필요할 때

 

404 페이지 (Not Found)

 

Next.js에서는 자동으로 not-found.tsx를 만들면 404 페이지를 처리할 수 있음. 굿

// app/not-found.tsx
export default function NotFound() {
  return <h1>😢 페이지를 찾을 수 없습니다.</h1>;
}

 

이제 존재하지 않는 페이지로 가면 자동으로 이 컴포넌트가 표시됨

 

정리 (Next.js 앱 라우팅 핵심 개념)

파일 기반 라우팅 app/ 폴더에 page.tsx 추가하면 자동으로 페이지 생성
동적 라우팅 [id] 폴더로 동적 경로 가능 (/blog/:id)
병렬 라우팅 여러 개의 레이아웃을 병렬로 렌더링 가능
인터셉트 라우팅 기존 페이지를 가리지 않고 특정 조건에서 다른 UI 표시 가능 (ex. 모달)
404 페이지 not-found.tsx 파일로 404 처리 가능