Next.js parallel Routes 란?

모아나·2024년 8월 1일
0

회사에서 대시보드 메인페이지를 구현해야 하는데, 이 메인페이지에서 여러가지 차트 블록이 있고 이 블록마다 데이터 패칭을 각각 해주어야 하는 상황이다.

대시보드 메인페이지 한 라우트 안에서 저 패칭이 모두 일어난다면 상태관리도 복잡해질 뿐더러 더러운 코드가 될 것 같다고 생각했다.

공식문서를 보니 Parallel Routes라는 방식을 알게 됐고, 해법이 될 수 있을 것 같아서 학습해보았다.

Parallel Routes

next.js 공식문서

병렬 라우팅을 사용해 동일한 레이아웃에서 하나 이상의 페이지를 동시 또는 조건부로 렌더링이 가능하다. 앱에서 대시보드나 피드처럼 매우 동적인 섹션에서 유용하다.

예시처럼 대시보드에서 teamanalytics 페이지를 동시에 렌더링 할 수 있다.

slots

병렬 라우트는 slots을 이용하는데 slot이란 @folder 컨벤션으로 정의된 것이다.


위는 @analytics@team slot이 정의된 것이다.

slots은 공유된 부모 레이아웃에 props로 들어간다. 위 예시에서는 @analytics@team slot을 app/layout.ts 에서 props로 전달 받는다. children prop과 함께 병렬적으로 렌더링 될 수 있다.

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

참고로 slot은 route segement가 아니기 때문에 URL 구조에 영향을 끼치지 않는다. 예를 들어 /@analytics/views/views로 URL이 정해진다.

ㄴroute segment에 대한 그림

Active state and navigation

기본적으로 Next.js는 각각의 slot에 대해 active state 를 추적하고 있다. 하지만 slot에서 렌더되는 콘텐츠는 네비게이션의 타입에 의존하고 있다.

active State란 사용자가 특정 페이지나 컴포넌트를 보고 있는 상태를 말한다.

Soft Navigation : 클라이언트 사이드 네비게이션에서 Next.js는 부분적인 slot 내에서 서브페이지를 바꾸는 부분적인 렌더를 진행한다. 이때 다른 slot은 active를 유지한다.
Hard Navigation : 전체 페이지 로드 후 (브라우저 새로고침) Next.js는 현재 URL과 매칭되지 않는 slot들의 active state 를 결정하지 못한다. 대신에 매칭되지 않은 slot들은 default.js 을 렌더한다. 만약 default.js 가 없다면 404를 반환한다.

default.js

default.js 파일은 최초 로드나 새로고침 시 매칭되지 않은 slot에 대한 fallback 렌더파일이라고 생각하면 된다.

사진에서 @team/settings 를 가지고 있지만 @analytics는 가지고 있지 않다. 이 상황에서 /settings 로 네비게이트 하면 @team slot은 /settings 페이지를 렌더하지만 @analyticsdefault.js 를 보여줄 것이다.

또한 children은 묵시적인 slot이기 때문에 Next.js가 부모 페이지의 active state를 복구할 수 없는 경우 children의 기본 fallback으로 default.js파일을 생성해야 한다.

useSelectedLayoutSegments

parallelRoutesKey를 파라미터로 받는다. 이 파라미터는 slot 내에 active route segement를 읽는다.

'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"과 동일할 것이다.

사용예시 : Conditional 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}</>
}

Tab Groups

layout.js를 slot 내부에 넣어 유저가 slot을 독립적으로 네비게이트할 수 있도록 한다.

예를 들어, @analytics/page-views/visitors 두 가지 서브 페이지를 가진다.

//app/@analytics/layout.tsx
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 내부에 layout 파일을 만들어 두 페이지간 탭을 공유한다.

공식문서 링크
Intercepting Routes와 함꼐 사용되어 모달을 구현할 수 있다. 이는 우리가 모달을 만들 때 겪었던 몇 가지 어려움들을 해결해준다

  • URL을 통해 공유 가능한 모달을 만들어준다
  • 페이지가 리프레시되어도 모달이 닫히지 않고 컨텍스트를 유지해준다
  • 뒤로가기 버튼을 눌러 모달을 닫을 수 있게 해준다
  • 앞으로가기 버튼으로 모달을 다시 열 수 있게 해준다

이 내용으로 미루어보아 폼형식, 특정 데이터를 띄워주는 모달에서 활용하면 좋겠다는 생각이 들었다.
그냥 경고나, 확인용도의 모달의 경우는 여러 컴포넌트에서 활용되다보니 라우터로 관리하면 오히려 복잡할 것 같다는 생각이 들었고 굳이 저 기능들이 필요로 하진 않은 것 같았다.

모달에 관해서는 intercepting routes 개념 또한 필요로 하여 다음 포스트에서 다뤄보도록 하겠다!

Loading and Error UI

병렬적 라우트는 독립적인 에러와 로딩을 각 라우트마다 가능하도록 한다.

이를 활용한다면, 대시보드 차트 블록마다 데이터 패칭의 상태가 어떤지 다른 차트 블록에 영향받지 않고 보여줄 수 있을 것 같아서 꼭 도입해봐야겠다!

예제 코드

https://www.youtube.com/watch?v=wi8kF8UniUI
위 유튜버가 아주 잘 예제코드를 설명해주어서 스택블리츠로 따라 구현해보았다.
스택블리츠 코드

궁금증 메모

  • slot대신 컴포넌트를 suspense를 잘써서 사용하는 것과 무슨 차이일까?
    : 현재로선 파일구조를 한 눈에 파악하기 좋기 위해 사용하는 것이라고 생각한다.
profile
Make things

0개의 댓글