JIHYEONJEONG
Next.js

nextjs에서 tab 내비게이션 구현

nextjs에서 tab 내비게이션을 구현하는 방법에 대해

예제

Problem - Nextjs에서 탭 라우팅 구조를 어떻게 만들 수 있을까?

Nextjs에서는 여기에 딱 맞는 예제를 찾을 수가 없었다.

Goal - Nextjs에서 Tab 라우팅을 구현해주세요. 각 탭은 url 세그먼트로 구분해야 합니다.

Implementation-1 일단 내가 아는 방법대로 구현하고, 다음에 더 나은 방법을 찾아보기

Next docs 서칭 결과 내가 찾을 수 있는 가장 빠른 방법은 Catch-All Route 을 사용한 방법이었다.


src/
└── app/
    └── invest/
        └── [...slug]/          <-- Catch-all segment
            └── page.tsx        <-- 해당 url을 모두 소비할 페이지
        └── layout.tsx          <-- 탭간의 이동을 위한 layout
    └── _components/            
        ├── Stock.tsx
        ├── DelayedStock.tsx
        ├── FinancialAudit.tsx
        └── Header.tsx
    └── _hooks/
        └── useLastPath.ts

해당 폴더 구조를 통해 모든 /invest 이하의 페이지를 위 layout과 page를 거치도록 작성했다.


'use client';

const CatchallTabPage = () => {
  const slug = useSlug();
  const renderContent = () => {
    switch (slug) {
      case 'stock':
        return <Stock />;
      case 'delayed-stock':
        return <DelayedStock />;
      case 'audit':
        return <FinancialAudit />;
      default:
        return <Stock />;
    }
  };

  return (

    <article className="min-h-screen bg-slate-900">
      <Header />
      <main>{renderContent()}</main>
    </article>

  );

};

  

export default CatchallTabPage;

내가 생각할 수 있는 가장 리액트스럽지만 URL 변화를 감지할 수 있는 탭을 완성했다.

TRADEOFF

  1. 최상위 page에서 state를 사용하기 때문에 app router의 서버 컴포넌트의 이점을 활용할 수 없음. e.g.) SEO 최적화
  2. slug값을 통해서 렌더되는 컴포넌트가 복잡해지고 숫자가 많아지는 경우, 생각해야 할 것이 많아진다. e.g.) 모든 컴포넌트를 한번에 렌더하므로 nextjs의 장점인 라우트 별 로딩, 에러 페이지를 따로 보여줄 수 없다.

Impementation-2 Nextjs의 병렬 라우트

당시 이해하지 못했던 병렬 라우트 를 다시 읽어보기로 했다.

여기서 핵심은, @접두사를 통해서 page, layout, error, loading, default까지 모든 시나리오를 라우트별로 따로 관리할 수 있다는 것이다.


parallel
│  default.tsx
│  layout.tsx
│  page.tsx

├─@tabs
│  │  layout.tsx // 
│  │  page.tsx
│  │
│  ├─audit
│  │      page.tsx
│  │
│  ├─stock
│  │      default.tsx // 상술한 것처럼, 가능한 시나리오를 탭별로 관리할 수 있음.
│  │      error.tsx
│  │      loading.tsx
│  │      page.tsx
│  │
│  └─top
│          page.tsx

└─_components
	 Header.tsx

//./layout.tsx // 위 @tabs로 구성된 라우팅들을 받아서 렌더링하는 역할.

export default async function Layout({

  params,

  tabs,

}: {

  params: any;

  tabs: React.ReactNode;

}) {

  return (

    <article>

      {tabs}

    </article>

  );

}

// ./page.tsx // 렌더링하는 것은 아래의 @tabs 이하이므로, 여기서는 굳이 렌더하지 않음.

const page = () => null;
export default page;
// /@tabs/layout.tsx // 실제로 @tabs/slug 내부의 컴포넌트를 렌더하는 역할.
export default function Layout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: any;
}) {
  return (
    <>
      {children}
    </>
  );
}


// /@tabs/page.tsx // 위 케이스와 동일함.

const page = () => null;
export default page;

와 같은 형태로, 서버 컴포넌트의 이점을 살리면서 구현할 수 있었다.

Summary

Nextjs에서 탭 라우팅을 구현하는 방법에 대해 고민해보고, 내가 생각하는 가장 빠른 방법과 Nextjs의 공식 문서에서 제시하는 방법을 모두 구현해보았다. Nextjs의 병렬 라우트 기능을 통해, 서버 컴포넌트의 이점을 살리면서도 깔끔한 탭 라우팅 구조를 만들 수 있었다.

Refs

nextjs catch-all segments nextjs parallel routes

On this page