[React-Router]outlet으로 multiple-layout 적용시키기

kyoung·2024년 4월 19일

개요

진행하고 있는 프로젝트에서 한 개의 웹서비스 내에 세 종류의 레이아웃을 사용하게 되었다.
이 때, tab bar와 navigation은 position: fixed; 를 사용해서 화면에 고정시킬 예정이다.

//main layout
    <>
      <Navigation />
      <Outlet />
      <Footer />
    </>
// main layout + tab bar
    <>
      <Navigation />
      <Outlet />
      <TabBar />
      <Footer />
    </>
// only body
    <>
      <Outlet />
    </>

이 구조로 멀티 레이아웃을 구현하는 방법을 기록해보고자 한다.

outlet?

구현 방법을 소개하기 전 공통적으로 사용되는 outlet 에 관해 간단히 정의해봐야 할 것 같다.
(공식 문서) 에는 이렇게 적혀있다.

An should be used in parent route elements to render their child route elements. This allows nested UI to show up when child routes are rendered. If the parent route matched exactly, it will render a child index route or nothing if there is no index route.

부모의 경로에 포함된 자식 요소들을 렌더링 한다고 한다.
직접 사용 해 보면서 이해해보기로 했다.

구현

구현 코드

각각의 레이아웃 컴포넌트를 생성하는 방법이다.
구체적인 페이지 path를 생각 해 두지 않아서, 임의로 페이지를 나눴다.

//App.tsx
function App() {
  return (
    <div className="app">
        <Routes>
          <Route element={<MainLayout />}>
            <Route path="/" element={<Main />} />
            <Route path="/profile" element={<>프로필</>} />
          </Route>
          <Route element={<TabBarLayout />}>
            <Route path="/rank" element={<>랭킹</>} />
            <Route path="/challenge" element={<>챌린지</>} />
          </Route>
          <Route path="/login" element={<>내 모임 바로가기</>} />
          <Route path="/livechat" element={<>실시간 채팅</>} />
        </Routes>
    </div>
  );
}

export default App;

outlet을 사용해서 각 레이아웃에 자식 요소로 내려준다.

body만 보여주는 레이아웃의 경우, 컴포넌트를 outlet을 통해 자식요소로 내려줄 필요는 없다고 생각해서, 밖으로 빼버렸다.

//MainLayout.tsx
export default function MainLayout() {
  return (
    <>
      <Navigation />
      <Outlet />
      <Footer />
    </>
  );
}
///TabBarLayOut.tsx
export default function TabBarLayout() {
  return (
    <>
      <Navigation />
      <Outlet />
      <TabBar />
      <Footer />
    </>
  );
}

구현에 성공했다. 페이지 이동 시 레이아웃이 잘 바뀌는걸 확인할 수 있다.

한 컴포넌트 내에서 제어하기

한 개의 컴포넌트로 줄이는 코드를 작성해봤다.

//App.tsx
function App() {
  return (
    <div className="app">
      <div id="wrapper">
        <Routes>
          <Route element={<Layout />}>
            <Route path="/" element={<Main />} />
            <Route path="/profile" element={<Main />} />
            <Route path="/rank" element={<Main />} />
            <Route path="/challenge" element={<Main />} />
          </Route>
          <Route path="/myclub" element={<>내 모임 바로가기</>} />
          <Route path="/livechat" element={<>실시간 채팅</>} />
          <Route path="/createclub" element={<>모임 만들기</>} />
          <Route path="/setting" element={<>설정 페이지 />
        </Routes>
      </div>
    </div>
  );
}

export default App;
//Layout.tsx
const INCLUDE_TAB_BAR_PATH = ['/rank', '/challenge'];

export default function Layout() {
  const path = useLocation().pathname;

  return (
    <>
      <Navigation />
      <Outlet />
      {!INCLUDE_TAB_BAR_PATH.includes(path) && <TabBar />}
      <Footer />
    </>
  );
}
  1. 탭바를 포함하는 페이지 path를 배열에 넣는다.
  2. 현재 사용자가 보고있는 페이지의 pathname을 useLocation() 훅을 사용해 가져와 path에 할당한다.
  3. INCLUDE_TAB_BAR_PATH.includes(path) && <TabBar />를 통해, 탭바를 포함한 path만 <TabBar /> 컴포넌트를 보여줄 수 있도록 한다.

 

0개의 댓글