오늘은 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 컴포넌트 사용하는 방법을 채택하고 나서 라우터에서 생긴 고민이 두 가지 있었습니다.
- 자식 라우트로 PostList를 사용하는 방법
- 각각의 정렬 페이지를 별도로 관리하는 방법
첫 번째 방법: 자식 라우트로 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" />
}
]
}
]
}
]);
'TIL' 카테고리의 다른 글
TIL- Supabase (1) | 2024.06.05 |
---|---|
TIL (Today I Learned) - 팀 프로젝트를 하면서 오늘 배운 내용 (0) | 2024.06.04 |
TIL - useMemo와 useCallback (0) | 2024.05.31 |
TIL: React, Vite, Redux(RTK), Router를 이용한 SPA 블로그 프로젝트 (0) | 2024.05.30 |
TIL - 전역상태관리 (Zustand) (0) | 2024.05.29 |