병렬 라우팅은 동시에 혹은 조건에 따라 동일한 레이아웃에서 하나 이상의 페이지를 렌더링하는 기능이다.
대시보드나 소셜 사이트의 피드와 같이 매우 동적인 앱 섹션에 대해, 병렬 라우팅을 사용하여 복잡한 라우팅 패턴을 구현할 수 있다.
예를 들어, 팀 페이지와 분석 페이지를 동시에 렌더링할 수 있다.
병렬 라우팅은 각 경로에 대해 독립적인 오류 및 로딩 상태를 정의할 수 있다. 이는 각 경로가 독립적으로 스트리밍되는 동안 사용된다.
병렬 라우팅은 또한 인증 상태와 같은 특정 조건에 따라 슬롯을 조건부로 렌더링할 수 있도록 한다. 이를 통해 동일한 URL에서 완전히 분리된 코드를 사용할 수 있다.
병렬 경로는 이름이 지정된 슬롯을 사용하여 생성된다. 슬롯은 @폴더
형식으로 정의되며, 동일 레벨의 레이아웃으로 props로 전달된다.
슬롯은 라우트 세그먼트가 아니며 URL 구조에 영향을 주지 않는다. 파일 경로 /@team/members
는 /members
에서 접근할 수 있다.
예를 들어, 다음과 같은 파일 구조는 @analytics
와 @team
두 개의 명시적인 슬롯을 정의한다.
위의 폴더 구조는 app/layout.js
컴포넌트가 @analytics
와 @team
슬롯 props를 받아들이고, 자식 props와 함께 병렬로 렌더링할 수 있도록 한다.
export default function Layout(props: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
);
}
💡 알아두면 좋은 점
children
props는 폴더에 매핑될 필요가 없는 암묵적인 슬롯이다. 이는 app/page.js
가 app/@children/page.js
와 동등하다는 것을 의미한다.
기본적으로, 슬롯 내에서 렌더링되는 내용은 현재 URL과 일치하다.
일치하지 않는 슬롯의 경우, Next.js가 렌더링하는 내용은 라우팅 기술과 폴더 구조에 따라 다르다.
default.js
default.js
파일을 정의하여 현재 URL을 기반으로 슬롯의 활성 상태를 복구할 수 없을 때 Next.js가 대체로 렌더링되도록 할 수 있다.
아래와 같은 폴더를 살펴보자. @team
슬롯은 settings
디렉토리를 가지고 있지만, @analytics
슬롯은 그렇지 않다.
루트 /
에서 /settings
로 이동한다면, 렌더링되는 내용은 네비게이션의 유형과 default.js
파일의 유무에 따라 다를 수 있다.
With @analytics/default.js | Without @analytics/default.js | |
---|---|---|
Soft Navigation | @team/settings/page.js and @analytics/page.js | @team/settings/page.js and @analytics/page.js |
Hard Navigation | @team/settings/page.js and @analytics/default.js | 404 |
💡 Soft Navigation
소프트 네비게이션 시에는 Next.js가 슬롯의 이전에 활성화된 상태를 렌더링한다. 이는 현재 URL과 일치하지 않더라도 해당 상태를 보존한다.
💡 Hard Navigation
페이지 전체를 다시 로드하는 네비게이션인 하드 네비게이션의 경우, Next.js는 먼저 일치하지 않는 슬롯의 default.js
파일을 렌더링하려고 시도한다. 이 파일이 없는 경우 404가 렌더링됩니다.
일치하지 않는 경로에 대한 404는 병렬로 렌더링되지 않아야 할 경로를 실수로 렌더링하지 않도록 도와준다.
useSelectedLayoutSegment
와 useSelectedLayoutSegments
는 parallelRoutesKey
를 받아들여 해당 슬롯 내에서 활성 경로 세그먼트를 읽을 수 있도록 한다.
// app/layout.tsx
'use client';
import { useSelectedLayoutSegment } from 'next/navigation';
export default async function Layout(props: {
//...
authModal: React.ReactNode;
}) {
const loginSegments = useSelectedLayoutSegment('authModal');
// ...
}
사용자가 @authModal/login
또는 URL 주소창에서 /login
으로 이동할 경우, loginSegments
는 "login"
이라는 문자열과 동일하다.
Modal 렌더링
병렬 라우팅은 모달을 렌더링하는 데 사용할 수 있다.
@authModal
슬롯은 일치하는 경로로 이동하면 표시될 수 있는 <Modal>
컴포넌트를 렌더링한다. 예를 들어 /login
과 같은 경로로 이동할 때 모달이 표시될 수 있다.
// app/layout.tsx
export default async function Layout(props: {
// ...
authModal: React.ReactNode;
}) {
return (
<>
{/* ... */}
{props.authModal}
</>
);
}
// app/@authModal/login/page.tsx
import { Modal } from 'components/modal';
export default function Login() {
return (
<Modal>
<h1>Login</h1>
{/* ... */}
</Modal>
);
}
모달이 비활성화되었을 때 모달의 내용이 렌더링되지 않도록 하려면, null
을 반환하는 default.js
파일을 생성할 수 있다.
export default function Default() {
return null;
}
Modal 닫기
클라이언트 네비게이션을 통해 모달이 시작된 경우, 예를 들어 <Link href="/login">
을 사용하여 모달을 표시한 경우, router.back()
을 호출하거나 Link
컴포넌트를 사용하여 모달을 닫을 수 있다.
'use client';
import { useRouter } from 'next/navigation';
import { Modal } from 'components/modal';
export default async function Login() {
const router = useRouter();
return (
<Modal>
<span onClick={() => router.back()}>Close modal</span>
<h1>Login</h1>
...
</Modal>
);
}
다른 곳으로 이동하고 모달을 닫으려면 catch-all route를 사용할 수도 있다.
// app/@authModal/[...catchAll]/page.js
export default function CatchAll() {
return null;
}
catch-all route는 default.js
보다 우선순위를 가진다.
조건부 라우팅
조건부 라우팅을 구현하기 위해 병렬 라우트를 사용할 수 있다.
예를 들어, 인증 상태에 따라 @dashboard
또는 @login
경로를 렌더링할 수 있다.
import { getUser } from '@/lib/auth';
export default function Layout({ params, dashboard, login }) {
const isLoggedIn = getUser();
return isLoggedIn ? dashboard : login;
}
[출처]
https://nextjs.org/docs/app/building-your-application/routing/parallel-routes