[Next.js 공식문서 정리] Routing : 08. Parallel Routes

2025. 3. 29. 13:57·웹 프로그래밍/Next.js
반응형

8. Parallel Routes (병렬 라우트)

병렬 라우트를 사용하면 동일한 레이아웃 내에서 여러 페이지를 동시에 또는 조건부로 렌더링할 수 있다. 이 기능은 대시보드, 소셜 사이트의 피드 등 매우 동적인 앱 섹션에 유용하다.

예를 들어, 대시보드에서 병렬 라우트를 사용하면 팀 페이지와 분석 페이지를 동시에 렌더링할 수 있다.

🔹 Slots (슬롯)

병렬 라우트는 슬롯(Slots) 이라는 개념을 사용해 구현 가능하다. 슬롯은 @폴더명 형식으로 정의된다.

예를 들어, 다음과 같은 폴더 구조는 @analytics, @team이라는 두 개의 슬롯을 정의한다.

app/
 ├── @analytics/
 │   ├── page.js
 ├── @team/
 │   ├── page.js
 ├── layout.js
 ├── page.js

이렇게 정의된 슬롯은 공유 부모 레이아웃(shared parent layout)의 props로 전달된다.
위의 예에서, app/layout.js는 @analytics, @team 슬롯을 props로 받아 children prop과 함께 병렬로 렌더링할 수 있다.

예제: app/layout.tsx

export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {children}  
      {team}  
      {analytics}  
    </>
  )
}

📌 중요한 점

  • slots은 라우트 세그먼트가 아니므로 URL에 영향을 주지 않는다.
    • 예를 들어, /@analytics/views의 URL은 /views이다. @analytics는 URL에 포함되지 않는다.
  • 동적(dynamic) 슬롯과 정적(static) 슬롯을 같은 레벨에서 사용할 수 없다.
    • 만약 하나의 슬롯이 동적 라우트라면, 해당 레벨의 모든 슬롯이 동적이어야 한다.

참고:
children 속성은 명시적으로 폴더에 매핑할 필요가 없는 암시적 슬롯(implicit slot) 이다.
즉, app/page.js는 app/@children/page.js와 동일하다.

 

🔹 Active State와 네비게이션

기본적으로 Next.js는 각 슬롯의 활성 상태(Active State) 를 추적한다.
하지만, 페이지 탐색 방식(Soft Navigation vs Hard Navigation) 에 따라 렌더링 방식이 달라진다.

📌 Soft Navigation (클라이언트 측 이동)

👉 클라이언트 사이드 내비게이션 시 발생하는 동작

  • Next.js는 페이지 전체를 새로고침하지 않고, 특정 부분(=슬롯 내의 서브페이지)만 부분적으로 변경(부분 렌더링(partial render))한다.
  • 예를 들어, 페이지 내 여러 슬롯이 있을 때, 한 슬롯의 내용이 바뀌더라도 다른 슬롯의 기존 내용은 그대로 유지된다.
  • 심지어 현재 URL과 일치하지 않더라도 기존 슬롯의 내용은 그대로 남아 있게 된다.
  • ✅ 쉽게 말하면: 페이지 일부만 바꾸고, 나머지는 유지하는 방식
  • 예) /dashboard에서 /dashboard/settings로 이동하면, @team과 @analytics 슬롯이 유지된다.

📌 Hard Navigation (새로고침 또는 직접 URL 입력)

👉 전체 페이지를 새로고침(브라우저 리프레시) 한 경우 발생하는 동작

  • 새로고침 시 Next.js는 현재 URL과 일치하는 슬롯만 렌더링할 수 있다.
  • 하지만 현재 URL과 일치하지 않는 슬롯이 있다면, Next.js는 그 슬롯의 활성 상태를 결정할 수 없다.
  • 따라서 Next.js는 해당 슬롯에 default.js 파일이 있으면 그것을 렌더링하고, default.js 파일이 없으면 404 페이지(페이지를 찾을 수 없음) 를 표시한다.
  • ✅ 쉽게 말하면: 새로고침하면 Next.js는 기존 상태를 모르므로, 기본값을 사용하거나(=default.js), 없는 경우 404를 띄운다.

🔹 default.js (기본 슬롯 컴포넌트)

슬롯이 처음 로드될 때 현재 URL에 일치하는 슬롯이 없다면, 해당 슬롯에 대한 기본 UI를 제공할 수 있다.
즉, Next.js가 슬롯의 활성 상태를 복구할 수 없을 때 default.js가 대체 렌더링된다.

📌 예제

다음 폴더 구조에서 @team 슬롯에는 /settings 페이지가 있지만, @analytics 슬롯에는 대응되는 페이지가 없다.

app/
 ├── @team/
 │   ├── settings/
 │   │   └── page.js
 │   └── page.js
 ├── @analytics/
 │   ├── default.js  <- 여기에 기본 UI 제공
 │   └── page.js
 ├── default.js  <- 여기에 기본 UI 제공
 ├── layout.js
 └── page.js
  • 사용자가 /settings로 이동하면 @team 슬롯은 /settings 페이지를 렌더링한다.
  • 하지만 @analytics 슬롯은 현재 경로에 맞는 페이지가 없으므로, default.tsx가 렌더링된다.
  • 만약 default.tsx가 없다면, @analytics 슬롯은 404 에러 페이지를 표시한다.
  • 또한, children은 암시적 슬롯(implicit slot) 이므로, Next.js가 부모 페이지의 활성 상태를 복구할 수 없을 때를 대비해 children의 대체 렌더링을 위한 default.js 파일을 생성해야 한다.

🔹 useSelectedLayoutSegment(s)

Next.js는 useSelectedLayoutSegment() 또는 useSelectedLayoutSegments()를 제공하여, 현재 활성화된 슬롯의 라우트 세그먼트를 읽을 수 있다.

📌 예제: useSelectedLayoutSegment() 사용

'use client'
import { useSelectedLayoutSegment } from 'next/navigation'

export default function Layout({ auth }: { auth: React.ReactNode }) {
  const loginSegment = useSelectedLayoutSegment('auth')  
  // 현재 활성화된 auth 슬롯이 'login'인지 확인 가능
}
  • 사용자가 /login으로 이동하면, loginSegment 값은 "login"이 된다.

🔹 병렬 라우트 활용 예시

✅ 1. 조건부 라우트 (Conditional Routes)

병렬 라우트를 사용하면 사용자 역할(Role)에 따라 다른 페이지를 렌더링할 수 있다.
예를 들어, /admin 또는 /user 역할에 따라 다른 대시 보드 페이지를 렌더링할 수 있다.

📌 예제

import { checkUserRole } from '@/lib/auth'

export default function Layout({
  user,
  admin,
}: {
  user: React.ReactNode
  admin: React.ReactNode
}) {
  const role = checkUserRole()
  return role === 'admin' ? admin : user
}
  • 관리자(Admin) 유저는 admin 슬롯을, 일반 유저는 user 슬롯을 렌더링한다.

✅ 2. 탭 그룹 (Tab Groups)

탭 메뉴를 만들 때 각 탭이 독립적으로 탐색 가능하도록 병렬 라우트를 활용할 수 있다.

📌 예제

app/
 ├── @analytics/
 │   └── page-views/
 │       └── page.js
 ├── visitors/
 │   └── page.js
 └── layout.js

@analytics 내에서 두 페이지 사이의 탭을 공유 할 layout 파일을 생성한다.

import Link from 'next/link'

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Link href="/page-views">Page Views</Link>
        <Link href="/visitors">Visitors</Link>
      </nav>
      <div>{children}</div>
    </>
  )
}
  • @analytics 슬롯 내부에서 /page-views와 /visitors를 독립적으로 탐색할 수 있다.

✅ 3. 모달 (Modals)

병렬 경로(Parallel Routes)는 인터셉팅 라우트(Intercepting Routes) 와 함께 사용되어 깊은 연결(deep linking) 을 지원하는 모달을 생성할 수 있다.
이를 통해 모달을 구축할 때 발생할 수 있는 일반적인 문제를 해결할 수 있다.

  • 모달 콘텐츠를 URL을 통해 공유할 수 있다.
  • 페이지가 새로 고침 될 때 모달을 닫는 대신, 모달의 컨텍스트를 보존한다.
  • 뒤로 가기(backwards navigation) 시, 이전 경로로 이동하는 대신 모달을 닫는다.
  • 앞으로 가기(forwards navigation) 시, 모달을 다시 열 수 있다.

🚀 UI 패턴 예시

사용자가 클라이언트 측 탐색을 사용하여 레이아웃에서 로그인 모달을 열거나, 별도의 /login 페이지에 접근할 수 있는 UI 패턴을 고려해보자.

이 패턴을 구현하려면, 먼저 기본 로그인 페이지를 렌더링하는 /login 경로를 생성해야 한다.

app/login/page.tsx

import { Login } from '@/app/ui/login';

export default function Page() {
  return <Login />;
}

그런 다음 @auth 슬롯 내부에 default.js 파일을 추가하여 null을 반환한다. 이렇게 하면 모달이 활성화되지 않았을 때 모달이 렌더링되지 않도록 할 수 있다.

app/@auth/default.tsx

export default function Default() {
  return null;
}

@auth 슬롯 내부에서 /login 경로를 가로채기 위해, (.)login 폴더를 업데이트한다.
<Modal> 컴포넌트와 그 자식 컴포넌트를 (.)login/page.tsx 파일에 import한다.

app/@auth/(.)login/page.tsx

import { Modal } from '@/app/ui/modal';
import { Login } from '@/app/ui/login';

export default function Page() {
  return (
    <Modal>
      <Login />
    </Modal>
  );
}

참고

  • 경로를 가로채기 위한 협약은 파일 시스템 구조에 따라 다르다. -> Intercepting Routes convention 참조
  • <Modal> 기능과 모달 콘텐츠(<Login>)를 분리하면, 모달 내부의 콘텐츠(예: forms)가 서버 컴포넌트로 처리된다. -> Interleaving Client and Server Components 참조

 

모달 열기

이제 Next.js 라우터를 활용하여 모달을 열고 닫을 수 있다. 이렇게 하면 모달이 열릴 때 URL이 올바르게 업데이트되고, 뒤로 가기 및 앞으로 가기에서 모달이 정확히 동작한다.

모달을 열려면, @auth 슬롯을 부모 레이아웃에 prop으로 전달하고, children prop과 함께 렌더링하면 된다.

app/layout.tsx

import Link from 'next/link';

export default function Layout({
  auth,
  children,
}: {
  auth: React.ReactNode;
  children: React.ReactNode;
}) {
  return (
    <>
      <nav>
        <Link href="/login">Open modal</Link>
      </nav>
      <div>{auth}</div>
      <div>{children}</div>
    </>
  );
}

사용자가 <Link>를 클릭하면 /login 페이지로 이동하는 대신 모달이 열린다.
하지만 새로고침 또는 초기 로드 시 /login 경로로 이동하면 사용자가 기본 로그인 페이지로 이동한다.

모달 닫기

모달은 router.back()을 호출하거나 <Link> 컴포넌트를 사용하여 닫을 수 있다.

app/ui/modal.tsx

'use client';

import { useRouter } from 'next/navigation';

export function Modal({ children }: { children: React.ReactNode }) {
  const router = useRouter();

  return (
    <>
      <button onClick={() => router.back()}>Close modal</button>
      <div>{children}</div>
    </>
  );
}

/login 외의 페이지로 이동 시 모달 닫기

Link 컴포넌트를 사용하여 @auth 슬롯을 더 이상 렌더링하지 않아야 하는 페이지로 이동하는 경우, 병렬 경로(parallel route)가 null을 반환하는 컴포넌트와 일치해야 한다.

예를 들어, 루트 페이지(/)로 이동하면 @auth/page.tsx가 실행되도록 만들고, 이 파일에서 null을 반환하여 모달이 사라지도록 한다.

app/ui/modal.tsx

import Link from 'next/link';

export function Modal({ children }: { children: React.ReactNode }) {
  return (
    <>
      <Link href="/">Close modal</Link>
      <div>{children}</div>
    </>
  );
}

app/@auth/page.tsx

export default function Page() {
  return null;
}

Catch-All 경로 사용

또는 다른 페이지(예: /foo, /foo/bar 등)로 이동하는 경우, catch-all 슬롯을 사용할 수 있다.

app/@auth/[...catchAll]/page.tsx

export default function CatchAll() {
  return null;
}

참고

  • @auth 슬롯에서 모달을 닫기 위해 catch-all 라우트를 쓰는 이유는 Active state and navigation에서 설명한 동작 방식 때문이다.
  • 클라이언트 사이드 내비게이션을 할 때, 원래 @auth 슬롯이 적용되던 경로에서 벗어나도 모달이 그대로 남아있을 수 있다.
  • 따라서 모달을 확실하게 닫으려면, @auth 슬롯이 특정 경로와 매칭되었을 때 null을 반환하도록 만들어야 한다. 이를 이용해 모달을 화면에서 사라지게 할 수 있다.

 

기타 예시

  • 갤러리에서 사진 모달을 열 때, 전용 /photo/[id] 페이지를 띄우기
  • 측면(side) 모달에서 쇼핑 카트를 열 때에도 이와 비슷한 방식을 적용 할 수 있음
  • 병렬 경로 및 가로채기 경로를 사용하여 모달을 구현한 예시로 다음 링크를 참고 -> 구현예시

🔹 로딩 및 에러 UI

각 슬롯은 개별적으로 스트리밍될 수 있으며, 각 슬롯마다 별도의 로딩/에러 UI를 설정할 수 있다.

📌 정리

✅ 병렬 라우트는 동적인 UI(대시보드, 탭, 모달 등)를 구현하는 데 유용한 기능이다.

 

 

 


https://nextjs.org/docs/app/building-your-application/routing/parallel-routes

 

Routing: Parallel Routes | Next.js

Simultaneously render one or more pages in the same view that can be navigated independently. A pattern for highly dynamic applications.

nextjs.org

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'웹 프로그래밍 > Next.js' 카테고리의 다른 글

[Next.js 공식문서 정리] Routing : 09. Intercepting Routes  (0) 2025.03.29
[Next.js 공식문서 정리] Routing : 07. Dynamic Routes.md  (0) 2025.03.29
[Next.js 공식문서 정리] Routing : 06. Route Groups  (0) 2025.03.29
[Next.js 공식문서 정리] Routing : 05. Redirecting  (0) 2025.03.29
[Next.js 공식문서 정리] Routing : 04. Loading UI and Streaming  (2) 2025.03.29
[Next.js 공식문서 정리] Routing : 03. Error Handling  (0) 2025.03.29
'웹 프로그래밍/Next.js' 카테고리의 다른 글
  • [Next.js 공식문서 정리] Routing : 09. Intercepting Routes
  • [Next.js 공식문서 정리] Routing : 07. Dynamic Routes.md
  • [Next.js 공식문서 정리] Routing : 06. Route Groups
  • [Next.js 공식문서 정리] Routing : 05. Redirecting
청량리 물냉면
청량리 물냉면
프로그래밍 공부를 하고 있습니다. 공부 내용 정리 겸 정보 공유를 목적으로 합니다.
    반응형
  • 청량리 물냉면
    노력중인 블로그
    청량리 물냉면
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • 프로그래밍 N
        • Programming
        • C | C++
        • Java N
        • Python
      • 웹 프로그래밍
        • HTML | CSS
        • JavaScript | TypeScript
        • React
        • Vue.js
        • Next.js
        • Spring & Spring Boot
        • JSP & Servlet
        • DB
      • 웹 프로젝트
        • 웹 프로젝트
        • 🥨스낵몰
        • 👨‍👨‍👧‍👧소셜 가계부
        • 🌜꿈 일기장
        • 🔮포트폴리오 사이트
        • 🏃‍♂️팀 프로젝트: 일정관리 프로그램
        • 📈팀 프로젝트: AI기반 주식 분석 플랫폼
        • 😺Just Meow It: 고양이의 조언
      • 앱 프로그래밍
        • Flutter
        • Kotlin
      • Problem Solving
        • 백준
        • 프로그래머스
        • SWEA
      • Computer Science
        • 알고리즘
        • 컴퓨터 네트워크
        • 이산수학
      • Developer
        • 후기
        • 자료정리
        • 취업 | 취준
        • 웹개발 교육 프로그램
        • TIL
  • 블로그 메뉴

    • 홈
    • Github
  • 공지사항

    • 프로그래밍 공부 중😊
  • 인기 글

  • 태그

    Next.js
    프로그래머스
    SWEA
    Jiraynor Programming
    ZeroCho
    백준
    클론 프로젝트
    spring boot
    플러터
    컴퓨터네트워크
    블로그 제작
    뉴렉처
    mysql
    bfs
    파이썬
    알고리즘
    Til
    강의내용정리
    타입스크립트
    d3
    프로젝트
    포트폴리오
    자바
    공식문서
    구현
    AWS
    리액트
    React
    자바스크립트
    웹사이트
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
청량리 물냉면
[Next.js 공식문서 정리] Routing : 08. Parallel Routes
상단으로

티스토리툴바