Next.js의 고오급 라우팅 패턴

sujin·2025년 3월 18일
0
post-thumbnail

Next.js 공부를 하다가 새로운 라우팅 패턴에 대해 알게 되었습니다. 이런게 가능하다니? 신기한 마음에 블로그로 정리해 보려고 합니다!


1️⃣ 패럴렐 라우트

발음하기도 어려운(?) 패럴렐 라우트는 병렬 라우트를 뜻합니다. 즉, 하나의 화면 안에 여러 페이지 컴포넌트를 한번에 렌더링 시켜주는 패턴입니다. 주로 트위터와 같은 소셜 미디어, 어드민 대시보드 등 복잡한 구조에 사용됩니다.

예시 이미지

Slot(슬롯)

패럴렐 라우트에는 슬롯 이라는 개념이 등장하는데요. 이는 병렬로 렌더링 될 페이지들을 보관하는 폴더 역할을 합니다. 원하는 페이지 폴더 아래 @ 를 붙여서 슬롯을 만들어 주면 됩니다. 이때, 생성된 슬롯은 라우트 그룹처럼 URL경로에는 아무런 영향을 주지 않습니다.

슬롯

슬롯의 특징에 대해서 몇가지 더 살펴보자면,

1. 부모 컴포넌트(layout.tsx)에 자동으로 props(슬롯에 생성한 컴포넌트)로 전달됩니다.

export default function Layout({
  children,
  sidebar,
  feed,
}: {
  children: React.ReactNode;
  sidebar: React.ReactNode;
  feed: React.ReactNode;
}) {
  return (
    <div>
      {feed}
      {sidebar}
      {children}
    </div>
  );
}

2. 개수 제한이 없습니다.

  • 하나의 페이지 디렉토리 안에 여러 개의 스롯을 만들 수 있습니다.

3. 슬롯 안에서 새로운 페이지를 추가할 수 있습니다.

  • 해당 슬롯 내부에서 페이지 전환이 가능하지만 바로 그 경로로 접근하거나 새로 고침을 하게되면 404페이지를 만나게 됩니다.

3번 특징에 대해 좀더 자세히 알아보겠습니다.

슬롯 안에서 새로운 페이지를 추가한 경우

@feed 슬롯 안에 setting 이라는 페이지 폴더를 생성한 뒤 layout.tsx 에 해당 페이지로 이동할 수 있는 테스트 경로를 추가했습니다.

// app/parallel/layout.tsx 

import Link from "next/link";

export default function Layout({
  children,
  sidebar,
  feed,
}: {
  children: React.ReactNode;
  sidebar: React.ReactNode;
  feed: React.ReactNode;
}) {
  return (
    <div>
      <div>
        <Link href={"/parallel"}>parallel</Link>
        &nbsp;
        <Link href={"/parallel/setting"}>parallel/setting</Link>
      </div>

      <br />
      {feed}
      {sidebar}
      {children}
    </div>
  );
}
를 클릭하면 Next.js의 클라이언트 라우팅이 작동하게 되는데 이때, Next.js는 기존 페이지에서 새로운 슬롯(parallel/@feed/setting/page.tsx)을 동적으로 불러와서 렌더링하게 됩니다.

즉, 현재 layout.tsx가 유지된 상태에서 패럴렐 라우트 내부만 업데이트 되기 때문에 정상적으로 동작합니다.

2. 초기 진입 (새로 고침)한 경우

Next.js는 페이지 진입시 해당 페이지가 독립적인 페이지인지를 확인하는데 슬롯 안에 등록된 페이지는 직접 페이지로 등록하지 않기 때문에 존재하지 않는 경로라고 판단하는 것입니다.

즉, 페러렐 라우트 내부 페이지들은 기본적으로 단독으로 접근할 수 없고 반드시 상위 layout.tsx 가 존재해야 정상적으로 렌더링됩니다.

3. 해결 방법

위와 같은 오류를 해결하는 방법은 간단합니다. 페럴렐 라우트가 비어있을 때 기본적으로 보여줄 페이지인 default.tsx 를 만들어 주면 됩니다.

export default function Default() {
  return <div>/parallel/default</div>;
}


2️⃣ 인터셉팅 라우트

인터셉팅 라우트는 intercept 의 낚아채다 라는 뜻대로 경로를 낚아채는 것을 말합니다. 즉, 사용자가 동일한 경로로 접속하게 되더라고 특정 조건에 만족하면 다른 페이지를 렌더링하도록 설정하는 기술 을 말합니다.

그런데 여기서 말하는 조건은 개발자가 직접 설정할 수 있는 것은 아니고, Next.js 에서 지정된 조건으로 초기 접속이 아닐 때 를 말합니다. 예를 들면 클라이언트 사이드 렌더링 방식 으로 <Link> 혹은 Router.push() 을 말합니다.

설정 방법

  1. app 폴더 아래 인터셉팅 라우트를 적용시킬 폴더를 만듭니다.
  2. 해당 폴더 앞에 (.) 를 붙여줍니다.
    • () 뒤에 나오는 경로를 인터셉트 하라는 의미입니다.
    • . 은 상태 경로를 나타냅니다. (동일한 경로라는 뜻)
    • (..) 인터셉트를 적용시킬 페이지의 경로가 한 단계 위에 있을 때
    • (..)(..) 인터셉트를 적용시킬 페이지의 경로가 두 단계 위에 있을 때
    • (...) app폴더 바로 아래에 있는 페이지를 인터셉트하겠다는 의미
      자세한 내용 보러가기

3️⃣ 패럴렐 라우트와 인터셉팅 라우트 함께 사용하기

이 두 가지 원리를 사용해서 인스타그램의 상세 페이지를 선택했을 때 동작하는 방식을 구현할 수 있습니다.

예시로 인스타그램(pc)에서 내 프로필에서 피드를 선택했을 때 피드 상세가 모달로 노출되지만, 다시 새로 고침을 하게되면 피드 상세 페이지로 보여지고 있습니다. 이와 같은 동작을 패럴렐 라우트와 인터셉팅 라우트를 사용해서 구현해 보도록 하겠습니다.

구현 동작

  1. 메인 페이지에서 책을 클릭하면 모달 형태로 책 상세 정보가 나타난다.
  2. 새로 고침하면 책 상세 페이지로 이동한다.

코드 구조

  • app/page.tsx → 메인 페이지
  • app/@modal/(.)book/[id]/page.tsx → 모달을 통한 책 상세 페이지
  • app/book/[id]/page.tsx → 책 상세 페이지
// app/page.tsx
export default function Home() {
  return (
    <div className={style.container}>
      <section>
        <h3>지금 추천하는 도서</h3>
        <div>
          {recoBooks.map((book) => (
              <Link href={`/book/${id}`} className={style.container}>
              	...
              </Link>
          ))}
        </div>
      </section>
    </div>
  );
}


// app/@modal/(.)book/[id]/page.tsx
import BookPage from "@/app/book/[id]/page";
import Modal from "@/components/modal";

export default function Page(props: any) {
  return (
    <Modal>
      <BookPage {...props} />
    </Modal>
  );
}

// app/book/[id]/page.tsx
export default function Page({ params }: { params: { id: string } }) {
  return (
    <div className={style.container}>
      <BookDetail bookId={params.id} />
      <ReviewEditor bookId={params.id} />
      <ReviewList bookId={params.id} />
    </div>
  );
}

정리

패럴렐 라우트와 인터셉팅 라우트를 함께 사용하면, 기존 페이지에서 모달을 띄우는 사용자 경험을 제공하면서도 새로 고침 시 개별 상세 페이지로 자연스럽게 이동하는 동작을 구현할 수 있습니다.

1. 메인 페이지에서 책을 클릭하면 모달이 열림 (인터셉팅 라우트)

  • app/@modal/(.)book/[id]/page.tsx가 렌더링됩니다.
  • 이 파일은 app/book/[id]/page.tsx를 불러와 <Modal> 로 감싸서 모달 형태로 표시됩니다.

2. 새로 고침하면 모달이 사라지고 책 상세 페이지로 이동 (패럴렐 라우트)

  • /book/[id]로 직접 접근하는 형태가 되면 app/book/[id]/page.tsx가 단독 렌더링됩니다. 즉, 모달이 아닌 전체 페이지로 전환됩니다.

3. @modal 패럴렐 라우트와 default.tsx 역할

  • / 로 접근하거나 /book/[id] 에 직접 접근했을 경우를 위해 default.tsx 을 만들어 줘야합니다.
  • app/@modal/default.tsx는 기본적으로 null을 반환합니다. → 모달이 필요 없을 때 아무것도 렌더링하지 않는 역할을 합니다.
export default function Default() {
  return null;
}

📚 참고
한 입 크기로 잘라먹는 Next.js(v15)

profile
개발댕발

0개의 댓글