[번역] Next.js13 App Router - Routing - Parallel Routes

최영호·2023년 7월 30일
2

On this page

병렬(parallel) 라우팅은 하나의 레이아웃에 하나 이상의 페이지를 동시에 혹은 조건부로 렌더링 할 수 있는 방법입니다.
대시보드, SNS의 피드 같은 굉장히 다이나믹한 섹션이 있는 앱을 위해 병렬 라우팅은 복잡한 라우팅 패턴을 구현할 수 있게 도와줍니다.

예를 들어 team 페이지와 analytics 페이지를 동시에 렌더링 할 수 있습니다.

병렬 라우팅은 각각의 라우트가 독립적으로 스트리밍 되기 때문에 독립적인 에러와 로딩 스테이트를 정의할 수 있도록 합니다.

병렬 라우팅은 회원 인증 스테이트 같은 특정 조건에 따라 결정 되는 슬롯을 조건부로 렌더링 할 수 있도록 도와줍니다.
이는 동일 URL에서 완벽하게 분리 된 코드를 사용할 수 있도록 합니다.

Convention

병렬 라우팅은 이름 있는 슬롯(names slots) 으로 만들 수 있습니다.
슬롯은 @folder 컨벤션으로 정의 되고 동일 레벨 레이아웃에 프롭으로 전달 됩니다.

슬롯은 라우트 세그먼트가 아니며 URL 구조에 영향을 미치지 않습니다. /@team/members 파일 패스는 /members 로 접근 가능합니다.

예를 들어 다음 정의 된 폴더 구조는 @analytics , @team 라는 2개의 슬롯을 가지고 있습니다.

위의 폴더 구조는 app/layout.js 컴포넌트는 @analytics@team 슬롯을 프롭으로 접근할 수 있고 children 프롭과 더불어 병렬 렌더링을 처리할 수 있습니다.

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

참고 사항:
children 프롭은 암묵적 슬롯으로 폴더에 매핑 처리할 필요가 없습니다. 다시 말해 app/page.js 컴포넌트는 app/@children/page.js 와 동일합니다.

Unmatched Routes

기본적으로 슬롯에서 렌더링 되는 컨텐츠는 현재 URL과 매치 됩니다.

매치 되지 않는 슬롯의 경우 Next.js가 렌더링 한 컨텐츠는 라우팅 테크닉과 폴더 구조에 따라 달라질 수 있습니다.

default.js

default.js 컴포넌트를 정의하여 Next.js가 현재 URL을 기반하여 슬롯의 활성화 상태를 처리하지 못하는 경우 fallback 컴포넌트로 사용할 수 있습니다.

다음의 폴더 구조를 생각해 봅시다.
@team 슬롯은 settings 디렉토리를 가지고 있지만 @analytics 는 가지고 있지 않습니다.

루트 / 에서 /settings로 이동한다면 렌더링 된 컨텐츠는 네비게이션 타입과 default.js 파일에 따라 달라지게 됩니다.

@analytics/default.js 가 존재하는 경우@analytics/default.js 가 존재하지 않는 경우
소프트 네비게이션@team/settings/page.js 그리고 @analytics/page.js@team/settings/page.js 그리고 @analytics/page.js
하드 네비게이션@team/settings/page.js 그리고 @analytics/default.js404

Soft Navigation

소프트 네비게이션에선 Next.js는 현재 URL과 맞지 않더라도 슬롯의 이전 활성 스테이트를 렌더링 합니다.

Hard Navigation

하드 네비게이션에선 네비게이션 발생시 전체 페이지 로딩이 발생합니다.
Next.js는 먼저 매치 되지 않는 슬롯의 defualt.js 컴포넌트를 렌더링 합니다.
만약 존재하지 않는다면 404 컴포넌트를 렌더링 합니다.

매치 되지 않는 요소에 404 컴포넌트를 배치하는 건 병렬 렌더링 되지 않아야 할 라우트를 실수로 렌더링 하지 않도록 도와줍니다.

useSelectedLayoutSegment(s)

useSelectedLayoutSegmentuseSelectedLayoutSegments 모두 해당 슬롯 내에서 활성 라우트 세그먼트가 무엇인지 알 수 있는 parallelRoutesKey 값을 받습니다.

'use client'
 
import { useSelectedLayoutSegment } from 'next/navigation'
 
export default async function Layout(props: {
  //...
  auth: React.ReactNode
}) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}

유저가 @auth/login 혹은 주소창에 /login으로 접근하면 loginSegments 값은 "login" 스트링 값으로 변하게 됩니다.

Examples

Modals

병렬 라우팅은 모달을 렌더링 하는데 사용 될 수 있습니다.

@auth 슬롯은 <Modal> 컴포넌트를 렌더링 하는데, 이 컴포넌트는 /login 같은 매칭 되는 라우트에 네비게이션 할 때 보여집니다.

app/layout.tsx

export default async function Layout(props: {
  // ...
  auth: React.ReactNode
}) {
  return (
    <>
      {/* ... */}
      {props.auth}
    </>
  )
}

app/@auth/login/page.tsx

import { Modal } from 'components/modal'
 
export default function Login() {
  return (
    <Modal>
      <h1>Login</h1>
      {/* ... */}
    </Modal>
  )
}

모달의 컨텐츠가 비활성화시에 렌더링 되지 않기 위해 default.js 컴포넌트를 만들어 null 을 리턴하도록 합니다.

app/@auth/default.tsx

export default function Default() {
  return null
}

Dismissing a modal

<Link href="/login"> 를 사용하여 클라이언트 네비게이션을 통해 모달이 활성화 된다면 Link 컴포넌트를 사용하거나 router.back() 을 호출하여 모달을 취소할 수 있습니다.

app/@auth/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>
  )
}

모달에 대한 더 자세한 정보는 Intercepting Routes 페이지에서 소개 합니다.

다른 곳에서도 모달을 취소하고 싶다면 catch-all 라우트를 사용하면 됩니다.

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

export default function CatchAll() {
  return null
}

catch-all 라우트는 defualt.js 보다 우선 순위를 가집니다.

Conditional Routes

병렬 라우팅은 조건부 라우팅을 구현하는데 사용 될 수 있습니다.
예를 들어 회원 인증 스테이트에 따라 @dashboard 혹은 @login 라우트를 렌더링 할 수 있습니다.

app/layout.tsx

import { getUser } from '@/lib/auth'
 
export default function Layout({
  dashboard,
  login,
}: {
  dashboard: React.ReactNode
  login: React.ReactNode
}) {
  const isLoggedIn = getUser()
  return isLoggedIn ? dashboard : login
}

profile
무친 프론트엔드 개발자를 꿈꾸며...

0개의 댓글