병렬 라우팅(Parallel Routing)은 동시에 또는 조건에 따라 동일한 레이아웃에서 하나 이상의 페이지를 렌더링할 수 있게 해줍니다. 대시보드나 소셜 사이트의 피드와 같은 매우 동적인 앱 섹션에서는 병렬 라우팅을 사용하여 복잡한 라우팅 패턴을 구현할 수 있습니다.
예를 들어, @team 페이지와 @analytics 페이지를 동시에 렌더링할 수 있습니다.
병렬 라우팅을 사용하면 독립적으로 스트리밍되는 각 경로에 대해 독립적인 오류 및 로드 상태를 정의할 수 있습니다.
병렬 라우팅은 인증 상태와 같은 특정 조건에 따라 슬롯을 조건부로 렌더링할 수 있도록 해줍니다. 이를 통해 동일한 URL에서 완전히 분리된 코드를 사용할 수 있게 됩니다.
Convention
병렬 라우트는 명명된 슬롯(named slots)을 사용하여 생성됩니다. 슬롯은 @folder 컨벤션으로 정의되며, 같은 수준의 레이아웃으로 props로 전달됩니다.
슬롯은 라우트 세그먼트가 아니며, URL 구조에 영향을 주지 않습니다. 파일 경로 /@team/members는 /members에서 접근할 수 있습니다.
예를 들어, 다음과 같은 파일 구조는 @analytics와 @team이라는 두 개의 명시적인 슬롯을 정의합니다.
위의 폴더 구조는 app/layout.js 컴포넌트가 @analytics와 @team 슬롯 props를 받고, children prop과 함께 병렬로 렌더링할 수 있도록 합니다.
/*app/layout.tsx*/
export default function Layout(props: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
)
}
알아두면 좋은 점: children prop은 폴더에 매핑될 필요가 없는 암묵적인 슬롯입니다. 즉, app/page.js는 app/@children/page.js와 동등합니다.
Unmatched Routes
기본적으로 슬롯 내에서 렌더링되는 컨텐츠는 현재 URL과 일치합니다.
일치하지 않는 슬롯의 경우, 콘텐츠는 Next.js가 렌더링하는 라우팅 기술 및 폴더 구조에 따라 다릅니다.
default.js
현재 URL을 기반으로 슬롯의 활성 상태를 복구할 수 없을 때 Next.js가 대체로 렌더링할 default.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
Soft Navigation에서 Next.js는 현재 URL과 일치하지 않더라도 슬롯의 이전 활성 상태를 렌더링합니다.
Hard Navigation
전체 페이지 재렌더링이 필요한 Hard Navigation에서 Next.js는 먼저 일치하지 않는 슬롯의 default.js 파일을 렌더링하려고 시도합니다. 사용할 수 없는 경우 404가 렌더링됩니다.
일치하지 않는 라우트에 대한 404는 병렬 렌더링되지 않아야 하는 라우트를 실수로 렌더링하지 않도록 하는 데 도움이 됩니다.
useSelectedLayoutSegment(s)
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"과 같습니다.
Examples
Modals
병렬 라우팅은 모달을 렌더링하는 데 사용할 수 있습니다.
@authModal 슬롯은 일치하는 경로(예: /login)로 이동하여 표시할 수 있는 <Modal> 구성 요소를 렌더링합니다.
/* 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 파일을 만들 수 있습니다.
/* app/@authModal/login/default.tsx */
export default function Default() {
return null
}
모달에 대한 자세한 내용은 Intercepting Routes 섹션에서 다룹니다.
Dismissing a modal
모달 닫는 방법
모달이 <Link>컴포넌트나 useRouter()를 사용한 클라이언트 네비게이션을 통해 시작된 경우 router.back()을 호출하거나 <Link> 컴포넌트를 사용하여 모달을 해제할 수 있습니다.
/* app/@authModal/login/page.tsx */
'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라우트를 사용할 수도 있습니다. catch-all은 default.js보다 우선순위가 높습니다.
Conditional Routes
조건부 라우트
병렬 라우트는 조건부 라우팅을 구현하는 데 사용할 수 있습니다. 예를 들어 인증 상태에 따라 @dashboard 또는 @login 라우트를 렌더링할 수 있습니다.
/* app/layout.tsx */
import { getUser } from '@/lib/auth'
export default function Layout({ params, dashboard, login }) {
const isLoggedIn = getUser()
return isLoggedIn ? dashboard : login
}
느낀점
읽기만 했을때는 이해가 잘됐었는데 해당 문서의 내용을 코드로 구현 해보면 헷갈리는 부분이 많습니다.
- default.js파일의 사용조건: 두개 이상의 슬롯이 조건부 없이 사용중이고 각 슬롯 마다 구조가 다른 경우, hard navigation시 NextJS는 현재 URL과 일치하지 않는 슬롯을 해당 슬롯 안에 있는 default.js 파일로 대체 하기 시작합니다. 만약 default.js파일이 없다면 404 not found에러가 발생하므로, 해당 내용을 숙지하고 있지 않다면 삽질을 많이 할거 같습니다.
- 병렬 라우트를 이용한 모달 생성: 해당 문서에 있는 모달 생성 예제대로 코드를 구현 하면 우리가 생각 했던 대로 작동하지 않습니다. Intercepting Routes를 참고하고 깃헙 예제를 보는게 이해하기 쉽습니다.
Parallel Routes를 활용할 수 있는 다양한 사례
- 대시보드 및 관리자 패널: Parallel Routes를 사용하여 여러 개의 동시 렌더링 페이지를 구성할 수 있습니다. 예를 들어, 대시보드 페이지에는 여러 개의 섹션(예: 사용자 정보, 분석 데이터, 설정 등)이 있을 수 있고, 각 섹션은 별도의 폴더로 구성된 Parallel Routes로 렌더링될 수 있습니다.
- 커뮤니티 뉴스 피드: 복잡한 커뮤니티 플랫폼에서는 다양한 종류의 콘텐츠를 병렬로 렌더링해야 할 수 있습니다. 예를 들어, 피드에는 게시물, 댓글, 알림 등의 섹션이 있을 수 있고, 각 섹션은 별도의 Parallel Routes로 구성하여 동시에 렌더링할 수 있습니다.
- 상점 카테고리 및 제품 목록: 온라인 상점에서는 다양한 카테고리와 제품 목록을 병렬로 렌더링해야 할 수 있습니다. 예를 들어, 카테고리 페이지에는 여러 개의 카테고리 섹션과 각 카테고리에 속한 제품 목록 섹션이 있을 수 있습니다. 각 섹션은 별도의 Parallel Routes로 구성하여 동시에 렌더링될 수 있습니다.
- 멀티페이지 폼: 복잡한 멀티페이지 폼에서는 여러 단계를 병렬로 처리해야 할 수 있습니다. 예를 들어, 회원가입 폼에는 개인 정보 입력, 계정 설정, 프로필 사진 업로드 등의 단계가 있을 수 있고, 각 단계는 별도의 Parallel Routes로 구성하여 동시에 렌더링될 수 있습니다.
- 회원 전용 페이지: 인증된 사용자만 접근할 수 있는 회원 전용 페이지를 구성할 수 있습니다. 인증된 사용자가 로그인한 경우에만 해당 페이지를 렌더링하고, 인증되지 않은 사용자는 로그인 페이지나 다른 경로로 이동하도록 처리할 수 있습니다. 이를 위해 Parallel Routes를 사용하여 인증된 사용자에게만 보여지는 페이지와 그에 따른 라우팅을 처리할 수 있습니다.
- 권한 기반 페이지: 특정 권한을 가진 사용자만 접근할 수 있는 페이지를 구성할 수 있습니다. 예를 들어, 관리자 권한을 가진 사용자만 접근할 수 있는 관리자 패널이나 특정 그룹의 회원만 접근할 수 있는 그룹 페이지 등을 구현할 수 있습니다. Parallel Routes를 사용하여 사용자의 권한에 따라 적절한 페이지와 라우팅을 처리할 수 있습니다.
- API 엔드포인트 보호: 인증된 요청에 대해서만 접근이 허용된 API 엔드포인트를 구성할 수 있습니다. Parallel Routes를 사용하여 인증된 사용자만이 해당 API 엔드포인트를 호출하고, 인증되지 않은 요청에 대해서는 인가 오류를 반환하도록 처리할 수 있습니다.
- 페이지 내 권한 제어: 페이지 내에서 특정 부분이나 기능을 권한에 따라 제어하고 싶은 경우에도 Parallel Routes를 사용할 수 있습니다. 예를 들어, 특정 버튼이나 폼 요소를 인증된 사용자에게만 보여주거나 사용 가능하도록 처리하고 싶을 때 Parallel Routes를 활용하여 해당 부분을 동적으로 렌더링하고 제어할 수 있습니다.
원본
Playground
'기억보단 기록을 > Next JS (App Router)' 카테고리의 다른 글
[NextJS 13] Routing - Route Handlers & 활용방안 (0) | 2023.06.25 |
---|---|
[NextJS 13] Routing - Intercepting Routes(라우트 가로채기) (0) | 2023.06.12 |
[NextJS 13] Routing - Error Handling(에러처리) (0) | 2023.06.10 |
[NextJS 13] Routing - Loading UI and Streaming (0) | 2023.06.07 |
[NextJS 13] Routing - Dynamic Routes (0) | 2023.06.06 |