Web Devlopment/NextJs

#20. Parallel Routes

키모형 2025. 12. 28. 18:28
반응형

Parallel Routes?

Next.js(App Router)에서 하나의 URL(같은 layout 컨텍스트) 안에 여러 “페이지 트리”를 슬롯(slot)처럼 동시에(또는 조건부로) 렌더링하는 기능. 아래 대시보드 처럼 “왼쪽  유저분석, 매출현황 패널 + 오른쪽 공지 패널” 같이 서로 독립적으로 바뀌는 영역을 만들 때 사용하기 좋음. 넥스트.js+1

핵심 규칙

  • 슬롯은 @이름 폴더로 만든다 (예: @users, @renvenue, @notifications, @modal) 넥스트.js
  • @slot 폴더는 URL 경로에 포함되지 않는다 (라우트 세그먼트가 아님)
  • 같은 레벨의 layout.tsx가 슬롯들을 props로 받는다 (children은 기본 슬롯, 나머지는 named slot) 

Next.js v16부터는 모든 슬롯에 default.js/ts(x)가 필수 (없으면 빌드 실패)

 

app/
  dashboard/
    default.tsx               // dathboard/ 하위 폴더의 default를 담당
    layout.tsx
    page.tsx                  // children 슬롯 (기본)
    @users/
      page.tsx
      default.tsx             // v16 필수
      settings/
        page.tsx              // /dashboard/settings일 때 users 슬롯에만 존재(예시)
    @revenue/
      page.tsx
      default.tsx             // v16 필수
    @notifications/
      page.tsx
      default.tsx             // v16 필수
    settings/
      page.tsx                // /dashboard/settings (children)

 

v16 필수: default.tsx

// app/dashboard/default.tsx
export default function DashboardDefaultPage() {
  return <h1 className="font-bold text-2xl mb-4">Complex dashboard</h1>;
}

// app/dashboard/@users/default.tsx
export default function DefaultUsers() {
  return <div style={{ opacity: 0.7 }}>No users view</div>;
}

// app/dashboard/@revenue/default.tsx
export default function DefaultRevenue() {
  return <div style={{ opacity: 0.7 }}>No revenue view</div>;
}

// app/dashboard/@notifications/default.tsx
export default function DefaultNotifications() {
  return <div style={{ opacity: 0.7 }}>No notifications view</div>;
}
 

default.tsx가 왜 필요하냐?

소프트 네비게이션(Next Link 이동) 중엔 슬롯별 “현재 선택된 하위 페이지 상태”를 기억하는데,

새로고침(하드 로드) 하면 그 상태를 복원할 수 없어서 매칭 안 되는 슬롯은 default를 렌더하게 됨.

v16부터는 이 상황을 명확히 처리하려고 모든 슬롯에 default를 강제하고 있음. 넥스트.js+2 넥스트.js+2

예: /dashboard/settings에 들어갔을 때 @users/settings/page.tsx는 존재하지만 @revenue/settings가 없으면, 새로고침 시 @ revenue /default.tsx가 나온다. 넥스트.js

 

File-system conventions: default.js | Next.js

API Reference for the default.js file.

nextjs.org

 

 

* notifications/archive 페이지는 존재하고 users, revenue 하위에는 해당 경로 페이지가 존재하지 않은경우

- app/dashboard/@notifications/archived/page.tsx 존재 

- app/dashboard/@users/archived/page.tsx  없음 => page hard reload 시 default.tsx 표시

- app/dashboard/@revenue/archived/page.tsx 없음  => page hard reload 시 default.tsx 표시  

 

슬롯레벨의 default.tsx를 계속 같이 만드는 것에 대한 문의

1) 반드시 필요한 것과, 상황에 따라 필요한 것을 구분하시면 됩니다

✅ 슬롯 레벨의 default.tsx는 사실상 필수입니다

  • @users/default.tsx, @revenue/default.tsx 처럼 슬롯 폴더 바로 아래에 두는 default.tsx는
  • “새로고침(하드 리로드) / 직접 URL 입력” 시 그 슬롯이 매칭되지 않을 때의 fallback이므로,
  • 운영 관점에서는 “보험”이라 생각하시고 두시는 편이 안전합니다.

❓ 하위 세그먼트별로 page.tsx를 계속 만드는 것은 “목표 UX”에 따라 달라집니다

  • 예: /complex-dashboard/archived에서 @users 슬롯이 기존 화면을 유지해야 한다면
    → @users/archived/page.tsx가 필요할 수 있습니다.
  • 반대로 archived에서는 @users가 비어있어도 된다
    → @users/default.tsx만으로 충분합니다(추가 파일 불필요).

즉, “모든 세그먼트에 모든 슬롯을 맞춰준다”는 규칙이 아니라,
**“그 URL에서 어떤 슬롯이 어떤 UX를 보여줘야 하는지”**에 따라 달라집니다.


2) 파일 폭증을 막는 실전 패턴 3가지

패턴 A) “공통 페이지 재사용(Proxy page)”로 최소 파일만 둡니다

@users/archived/page.tsx를 새로 만들더라도, 내용은 중복하지 않고 “위임”만 하시면 됩니다.

// @users/archived/page.tsx
import UsersPage from "../page";
export default function UsersArchived() {
  return <UsersPage />;
}
  • 파일은 하나 더 생기지만, 실제 구현은 재사용이라 유지보수 부담이 매우 작습니다.
  • “이 URL에서는 기존 UI 유지” 같은 요구를 충족하기 좋습니다.

패턴 B) Catch-all 세그먼트로 “하위 뎁스 차이”를 흡수합니다

하위 뎁스가 계속 늘어날 수 있다면, 특정 슬롯을 이렇게 만들 수 있습니다.

 
@users/[...rest]/page.tsx
// @users/[...rest]/page.tsx
import UsersPage from "../page";

export default function UsersAnyDepth() {
  return <UsersPage />;
}
  • 이렇게 하면 /complex-dashboard/archived, /complex-dashboard/foo/bar 등
    어떤 세그먼트가 와도 해당 슬롯은 동일 UI로 대응할 수 있습니다.
  • 결과적으로 “세그먼트별 page.tsx 복사”를 크게 줄일 수 있습니다.

주의: catch-all은 강력하지만 “너무 많이 잡아먹는” 부작용도 있을 수 있어,
해당 슬롯이 정말 “모든 세그먼트에서 동일 UI를 유지해도 되는지”를 기준으로 적용하시는 것이 좋습니다.


패턴 C) “세그먼트는 children에서만 만들고, 슬롯은 default로 처리”합니다

가장 단순한 운영 패턴입니다.

  • URL 세그먼트들은 children 트리에서만 관리합니다.
    (예: complex-dashboard/archived/page.tsx는 반드시 존재)
  • 슬롯은 세그먼트별 page를 만들지 않고, 매칭 안 되면 default로만 처리합니다.

이 방식은 파일 수가 적고 구조가 단순하지만,
“특정 URL에서 슬롯도 전용 화면을 보여줘야 한다”는 요구가 생기면 확장이 필요합니다.


3) “자원 낭비”에 대한 결론

  • default.tsx 자체는 렌더링될 때만 의미가 있고, 파일이 있다고 해서 “런타임 자원”을 지속적으로 잡아먹는 구조는 아닙니다.
  • 진짜 부담은 “파일 개수 증가로 인한 관리 비용”인데,
  • 위의 Proxy 재사용 / catch-all 흡수 / children 중심 설계 중 하나를 선택하시면
    파일 폭증을 충분히 제어할 수 있습니다.

4) 추천 가이드 (현 상황 기준)

현재처럼 archived 같은 일부 세그먼트가 있고,
대부분의 슬롯은 “기존 UI 유지”가 목적이라면 보통은 아래 조합이 깔끔합니다.

  1. children 세그먼트는 반드시 존재: complex-dashboard/archived/page.tsx
  2. 각 슬롯은 최소 default.tsx 필수
  3. “특정 세그먼트에서도 그대로 유지”가 필요한 슬롯만
    • @slot/archived/page.tsx를 Proxy 방식으로 추가하거나
    • 정말 많이 늘어나면 @slot/[...rest]/page.tsx로 흡수
반응형