병렬 라우트는 같은 레이아웃 내에 하나 이상의 페이지를 조건부로 혹은 동시에 렌더링한다.
이것은 대시보드나 소셜 피드 같이 매우 다이나믹한 섹션에 유용하다.
예를들어 대시보드를 만들때 병렬 라우트를 사용하면 동시에 team과 analytics 페이지를 렌더링한다.
병렬 라우트는 slots 이라는 이름으로 생성된다. Slot은 @folder 컨벤션으로 정의 된다.
예를 들어 아래 파일 구조는 두 개의 slots을 정의한다. @analytics, @team
slots는 공유된 부모 레이아웃에 props로 전달된다.
예시에서는 app/layout.js의 컴포넌트가 @anlytics 및 @team slots props를 받고,children prop과 함께 병렬로 렌더링 된다.
export default function Layout({
children,
team,'analytics,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
slots은 route segments가 아니라서 URL구조에 영향을 끼치지 못한다.
예를들어 /@analytics/views는 URL이 /views가 된다. 왜냐하면 @analytics가 slot이기 때문이다.
slots은 일반 페이지 컴포넌트와 결합되어 route segment와 연결된 최종 페이지를 형성한다.
그렇기 때문에 동일한 route segment 수준에서 dynamic slots과 static slots을 분리할 수 없다.
만약 하나의 slot이 dynamic 이라면 해당 수준의 모든 slots은 dynamic 이다.
기본적으로 Next.js는 각 slot의 active state 를 추적한다.
그러나 한 슬롯 내에 렌더된 content는 네비게이션 타입에 의존한다.
Soft Navigation: client-side navigation 동안 Next.js는 부분 렌더링을 수행한다. 현재 URL과 일치하지 않더라도. 다른 slot의 활성화된 하위 페이지를 유지하면서 slot내의 하위페이지를 변경한다.
Hard Navigation: 전체 페이지 로드(브라우저 새로고침) 후에, Next.js는 현재 URL과 일치하지 않는 slots에 활성화된 state를 결정할 수 없다. 대신에 일치하지 않는 slots에 대해 default.js file 또는 404(default.js가 없을 때) 렌더 한다.
초기 로드 또는 full-page 로드 동안 매칭 되지 않는 slots을 위해서 fallback으로 렌더할 default.js 파일을 정의할 수 있다.
/settings로 이동할때 @team slot은 @analytics slot의 현재 활성화 된 페이지를 유지하면서 /settings 페이지에 렌더링 될것이다.
새로고침 하면 .Next.js는 @analytics에 default.js를 렌더링 할것이다. 만약 default.js가 없으면 404페이지를 렌더한다.
참고로 children은 암묵적으로 slot이기 때문에. Next.js가 부모 페이지에 현재 state를 복구할 수 없을때 children의 fallback을 렌더링 하기위해 default.js 을 만들어야한다.
useSelectedLayoutSegment와 useSelectedLayoutSegments는 parallelRoutesKey 파라미터를 받아 slot내의 활성화된 route segment를 읽을 수 있게 한다.
//app/layout.tsx
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
유저가 app/@auth/login으로(/login) 이동할때, loginSegment는 "login"이 된다.
병렬라우트를 사용하면 사용자 역할과 같은 특정 조건에 따라 routes를 조건부로 렌더링할 수 있다.
예를들어 /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
}
유저가 slot을 독립적으로 이동할 수 있도록 layout을 slot안에 넣을 수 있다.
@analytics 내에 두 페이지간 탭을 공유할 수 있는 layout file을 만든다.
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>
</>
)
}
병렬 라우트는 Intercepting Routes와 함께 deep linking을 지원하는 모달을 만들 수 있다.
이것은 모달을 만들때의 공통의 문제들을 해결할 수 있다.
사용자가 클라이언트 측 탐색을 사용하여 레이아웃에서 로그인 모달을 열거나 별도의 /login 페이지에 접근할 수 있는 UI 패턴을 고려해보라.
이 패턴을 수행하기 위해 먼저 기본 로그인을 수행하는 /login 라우트를 먼저 만들어라.
//app/login/page.tsx
import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
그런 다음 @auth slot안에 default.js 파일을 넣고 null을 리턴해라. 이렇게하면 이것이 활성화 되지 않았을때 모달을 렌더링하지 않는다.
//app/@auth/default.tsx
export default function Default() {
return null
}
@auth slot 내에서 /(.)login 폴더를 업데이트하여 /login 라우트를 가로챈다.
/(.)login/page.tsx 파일에 Modal 컴포넌트와 자식을 가져온다.
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
이제 Next.js 라우터를 활용하여 모달을 열고 닫을 수 있다.
이를 통해 모달이 열릴 때와 이전 및 이후 탐색 시 URL이 올바르게 업데이트 되도록 한다.
모달을 열려면, 부모 레이아웃에 @auth slot을 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 컴포넌트를 사용한다.
'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>
</>
)
}
Link컴포넌트를 사용하여 더 이상 @auth slot을 렌더링 하지 않아야하는 페이지로 이동할 때는 병렬 라우트가 null을 반환하는 컴포넌트와 일치하도록 해야한다.
예를 들어 루트 페이지로 돌아갈 때 @auth/page.tsx 컴포넌트를 생성한다,
//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 '...'
}
또 다른 페이지 (예: /foo, /foo/bar 등) 로 이동할때 catch-all slot을 사용할 수 있다.
//app/@auth/[...catchAll]/page.tsx
export default function CatchAll() {
return '...'
}
Parallel Routes는 독립적으로 스트리밍될 수 있으므로 각 라우트에 대해 독립적인 오류 및 로딩 상태를 정의할 수 있다.