본문 바로가기

TIL

TIL - 리액트 라우터 연결 방법: 문제와 해결

오늘은 React와 Supabase를 이용하여 맛집 공유 뉴스 피드 사이트를 구현하는 과정에서 겪었던 고민과 해결 방법에 대해 공유하려고 합니다. 라우팅과 상태 관리에 대해 중점적으로 다룰 것입니다.

 

프로젝트 구조

이 프로젝트는 Single Page Application(SPA)으로, React를 사용하여 구현되었습니다. React Router v6을 이용하여 클라이언트 사이드 라우팅을 관리하고 있으며, Supabase를 통해 데이터를 관리합니다.

 

 

문제 정의

1. 뉴스 피드 페이지(MainPage)에 세 가지 정렬 버튼이 있습니다: 트렌드, 최신, 팔로우. 이 버튼들을 클릭할 때 기준으로 정렬된 게시물이 표시되기를 원했습니다.

2. 사용자가 기본 URL(test.com)에 접속했을 때 리다이렉트되어 트렌딩 게시물을 보여주기를 원했습니다.

 

문제 접근 방식

먼저, 각 정렬 기준에 맞춰 데이터를 로드하고, 이를 PostList 컴포넌트로 props로 전달하여 표시하려고 했습니다.

 

1. Link 컴포넌트 사용하는 방법

function MainPage() {
  return (
    <div>
      <div>
        <Link to="/trending"><button>트렌드</button></Link>
        <Link to="/recent"><button>최신</button></Link>
        <Link to="/follow"><button>팔로우</button></Link>
      </div>
      <StrDiv>
        <Outlet />
      </StrDiv>
    </div>
  );
}

// 라우터

const router = createBrowserRouter([
  {
    path: '/',
    element: <MainLayout />,
    children: [
      {
        path: '',
        element: <Navigate to="trending" replace />
      },
      {
        path: 'trending',
        element: <MainPage sorting="trending" />
      },
      {
        path: 'recent',
        element: <MainPage sorting="recent" />
      },
      {
        path: 'follow',
        element: <MainPage sorting="follow" />
      }
    ]
  }
]);

 

장점 

  • URL이 명확하게 관리됩니다. 사용자가 URL을 북마크하거나 공유할 수 있습니다.
  • 브라우저의 앞 뒤로 탐색 기능을 사용할 수 있습니다.
  • SEO(검색 엔진 최적화)에 유리합니다.

단점

  • 상태 관리가 URL 기반으로 이루어지므로, 간단한 상태 변화에도 URL을 변경해야 합니다.

2. Button onClick으로 props 전달

 

function MainPage() {
  const [sorting, setSorting] = useState('trending');

  return (
    <div>
      <div>
        <button onClick={() => setSorting('trending')}>트렌드</button>
        <button onClick={() => setSorting('recent')}>최신</button>
        <button onClick={() => setSorting('follow')}>팔로우</button>
      </div>
      <StrDiv>
        <PostList sorting={sorting} />
      </StrDiv>
    </div>
  );
}

// 라우터
const router = createBrowserRouter([
  {
    path: '/',
    element: <MainLayout />,
    children: [
      {
        path: '',
        element: <MainPage />
      }
    ]
  }
]);

 

장점

  • 상태 관리가 컴포넌트 내에서 이루어져 간단합니다.
  • URL을 변경하지 않기 때문에 간단한 상태 변화에 적합합니다.

단점

  • URL이 상태를 반영하지 않기 때문에 사용자가 URL을 북마크하거나 공유할 수 없습니다. 
  • 브라우저의 앞으로/뒤로 탐색 기능을 사용할 수 없습니다.
  •  SEO에 불리합니다.

그래서 Link 컴포넌트 사용하는 방법을 채택하고 나서 라우터에서 생긴 고민이 두 가지 있었습니다.

  1. 자식 라우트로 PostList를 사용하는 방법
  2. 각각의 정렬 페이지를 별도로 관리하는 방법

 

첫 번째 방법: 자식 라우트로 PostList 사용

const router = createBrowserRouter([
  {
    path: '/',
    element: <MainLayout />,
    children: [
      {
        path: '',
        element: <MainPage />,
        children: [
          {
            path: 'trending',
            element: <PostList sorting="trending" />
          },
          {
            path: 'recent',
            element: <PostList sorting="recent" />
          },
          {
            path: 'follow',
            element: <PostList sorting="follow" />
          }
        ]
      }
    ]
  }
]);

 

두 번째 방법: 각각의 정렬 페이지를 별도로 관리

const router = createBrowserRouter([
  {
    path: '/',
    element: <MainLayout />,
    children: [
      {
        path: '',
        element: <Navigate to="trending" replace />
      },
      {
        path: 'trending',
        element: <MainPage sorting="trending" />
      },
      {
        path: 'recent',
        element: <MainPage sorting="recent" />
      },
      {
        path: 'follow',
        element: <MainPage sorting="follow" />
      }
    ]
  }
]);

 

비교 및 분석

1. 첫 번째 방법: 자식 라우트로 PostList 사용

 

장점

  • 코드 중복이 적어짐: PostList 컴포넌트를 재사용하여 코드 중복을 줄일 수 있습니다
  • 관리가 용이함: PostList가 MainPage의 자식으로 있어 상태 관리가 단순해집니다.

단점

  • URL 구조가 복잡해질 수 있음: MainPage 안에 여러 자식 라우트를 두는 구조가 복잡하게 느껴졌습니다.
  • 특정 페이지로 접근하기 위해서는 MainPage가 반드시 로드되어야 합니다.

 

2. 두 번째 방법: 각각의 정렬 페이지를 별도로 관리

 

장점

  • 각 페이지의 독립성: 각 정렬 기준 페이지가 독립적으로 관리되어 변경사항이 명확합니다.
  • URL이 더 직관적: 사용자가 접근하는 URL이 명확하게 각 페이지를 나타냅니다.

단점

  • 코드 중복 가능성: 각 정렬 페이지에서 유사한 코드가 반복될 수 있습니다.
  • 상태 관리 복잡성 증가: 각 페이지가 독립적으로 상태를 관리해야 하므로 상태 동기화가 복잡할 수 있습니다.

 

대부분의 경우, 첫 번째 방법이 더 유리합니다. 이유는

  • 코드 중복을 피할 수 있고, 유지보수가 용이합니다.
  • PostList 컴포넌트를 중앙에서 관리할 수 있어 상태 관리가 간단해집니다.
  • 리액트 라우터의 Outlet 기능을 활용하여 페이지 구성요소를 동적으로 로드할 수 있습니다.

 

하지만, 특정 페이지가 독립적으로 로드되고 관리되어야 하는 경우(예: 각 페이지마다 별도의 로직이 필요하거나 SEO 최적화가 중요한 경우)라면 두 번째 방법이 더 적합할 수 있습니다.

 

결론

첫 번째 방법을 통해 코드의 재사용성을 높이고 유지보수성을 향상시킬 수 있었습니다. 각 정렬 기준에 맞는 PostList 컴포넌트를 자식 라우트로 설정함으로써 라우팅을 간단하게 관리할 수 있었습니다. 

 

이렇게 설정함으로써 사용자가 test.com에 접속했을 때 자동으로 test.com/trending으로 리다이렉트되며, 각 버튼을 클릭할 때마다 URL이 변경되고, 해당 정렬 기준에 맞는 게시물이 표시됩니다

 

최종 코드

function MainPage() {
  return (
    <div>
      <div>
        <Link to="/trending"><button>트렌드</button></Link>
        <Link to="/recent"><button>최신</button></Link>
        <Link to="/follow"><button>팔로우</button></Link>
      </div>
      <StrDiv>
        <Outlet />
      </StrDiv>
    </div>
  );
}

const router = createBrowserRouter([
  {
    path: '/',
    element: <MainLayout />,
    children: [
      {
        path: '',
        element: <Navigate to="trending" replace />
      },
      {
        path: '',
        element: <MainPage />,
        children: [
          {
            path: 'trending',
            element: <PostList sorting="trending" />
          },
          {
            path: 'recent',
            element: <PostList sorting="recent" />
          },
          {
            path: 'follow',
            element: <PostList sorting="follow" />
          }
        ]
      }
    ]
  }
]);