Hello, World!
세상과 소통하는 개발자의 이야기
Design. 수민 / cc. 경희

[ React 블로그 개발 ] 6. Navigation(라우팅) 연동하는 방법

2025-09-09

route (navigation) 연동

 

1. 왜 Navigation이 필요할까?

React는 기본적으로 SPA(Single Page Application)이기 때문에, 페이지가 전환되는 것처럼 보여도 실제로는 하나의 HTML 파일 안에서 컴포넌트만 바뀝니다.

하지만 블로그, 쇼핑몰, 대시보드 등 실제 서비스에서는 URL에 따라 다른 화면을 보여주고, 사용자가 '뒤로가기', '앞으로가기' 등 네비게이션을 자연스럽게 할 수 있어야 합니다.

이를 위해 라우터 라이브러리를 사용합니다.


2. 대표적인 라이브러리

React에서 가장 많이 쓰는 라우터 라이브러리는 React Router입니다.

  • 공식 라이브러리, 커뮤니티도 큼
  • 다양한 기능: 동적 라우팅, 파라미터, 중첩 라우트, 네비게이션 등

3. 설치 방법

터미널에서 아래 명령어로 설치하세요.

npm install react-router-dom
# 또는
yarn add react-router-dom

4. 기본 사용법

4.1. 라우터 세팅 (최상위 컴포넌트에 BrowserRouter)

  • import { BrowserRouter } from "react-router-dom"
    
    function App() {
      return (
        <BrowserRouter> 
          <YourRoutes />
        </BrowserRouter>
      )
    }
    

4.2. Route 정의 및 페이지 컴포넌트 연결

  • import { Routes, Route } from 'react-router-dom'
    import Home from './Home'
    import About from './About'
    
    function YourRoutes() {
      return (
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          {/* 추가적으로 원하는 라우트 */}
        </Routes>
      )
    }
    

4.3. 네비게이션(페이지 이동) 방법

1) Link 컴포넌트 사용

  • import { Link } from 'react-router-dom';
    
    <Link to="/about">About 페이지로 이동</Link>
    

2) useNavigate 훅 사용 (코드로 이동)

  • import { useNavigate } from 'react-router-dom';
    
    function MyComponent() {
      const navigate = useNavigate();
    
      const goToAbout = () => {
        navigate('/about');
      };
    
      return <button onClick={goToAbout}>About으로 이동</button>;
    }
    

5. 라우트 파라미터와 쿼리스트링

5.1. 동적 파라미터

  • <Route path="/post/:postId" element={<PostPage />} />
    
  • /post/123 방문 시, postId123이 들어감
  • import { useParams } from 'react-router-dom';
    
    function PostPage() {
      const { postId } = useParams();
      // postId로 API 호출 등
    }
    

5.2. 쿼리스트링 읽기

  • import { useLocation } from 'react-router-dom';
    
    function SearchPage() {
      const location = useLocation();
      const query = new URLSearchParams(location.search);
      const keyword = query.get('keyword');
    }
    

6. 코드 적용하기

  • main.tsx

    • react-router-dom 을 저희 페이지에 적용시켜 줍니다.
    • import { StrictMode } from "react";
      import { createRoot } from "react-dom/client";
      
      import { BrowserRouter } from "react-router-dom";
      import {
        AppTopComponent,
        MainComponent,
        SidebarLeft,
        SidebarRight,
        TopComponent,
      } from "./components/index.ts";
      import "./index.css";
      import { Mobile, PC } from "./utils/MobileCheck.tsx";
      
      export const AppEntry = () => {
        return (
          <BrowserRouter>
            <div
              style={{
                width: "100%",
              }}
            >
              <PC>
                <TopComponent />
              </PC>
              <Mobile>
                <AppTopComponent />
              </Mobile>
              <div style={{ width: "100%", display: "flex", flexDirection: "row" }}>
                <PC>
                  <SidebarLeft />
                </PC>
                  <MainComponent />
                <PC>
                  <SidebarRight />
                </PC>
              </div>
            </div>
          </BrowserRouter>
        );
      };
      
      createRoot(document.getElementById("root")!).render(
        <StrictMode>
          <AppEntry />
        </StrictMode>
      );
      
  • src/screens/PostListScreen.tsx 를 추가시켜줍니다.

    • 저희 글의 리스트를 메뉴별로 따로 가져오게 하려고 합니다.
    • import { useState } from "react";
      import { useNavigate } from "react-router-dom";
      
      export default function SidebarLeft() {
        const navigation = useNavigate();
        const [activeMenu, setActiveMenu] = useState<number | null>(null); // 드롭다운 상태 관리
        const menus = [
          {
            no: 1,
            title: "React Menu 1",
            subMenus: [
              {
                no: 1,
                title: "React SubMenu 1-1",
              },
              {
                no: 3,
                title: "React SubMenu 1-2",
              },
            ],
          },
          {
            no: 2,
            title: "React Menu 2",
            subMenus: [
              {
                no: 2,
                title: "React SubMenu 2-1",
              },
              {
                no: 4,
                title: "React SubMenu 2-2",
              },
            ],
          },
        ];
      
        const toggleDropdown = (menuNo: number) => {
          setActiveMenu((prev) => (prev === menuNo ? null : menuNo)); // 클릭 시 드롭다운 토글
        };
      
        return (
          <div
            style={{
              alignItems: "center",
              justifyContent: "center",
              display: "flex",
              flexDirection: "column",
              width: "20%",
            }}
          >
            <div style={{ margin: 10, fontWeight: 900, fontSize: 17 }}>MENU</div>
            {menus?.map((menu) => (
              <div key={menu.no} style={{ width: "100%" }}>
                <div
                  onClick={() => toggleDropdown(menu.no)}
                  style={{
                    padding: "10px",
                    cursor: "pointer",
                    textAlign: "center",
                    backgroundColor:
                      activeMenu === menu.no ? "#f0f0f0" : "transparent", // 드롭다운 활성화 시 배경색 변경
                  }}
                >
                  {menu.title}
                </div>
                {activeMenu === menu.no &&
                  menu.subMenus?.map((subMenu) => (
                    <div
                      key={subMenu.no}
                      onClick={() => {
                        navigation(`/post/list/${subMenu.no}`);
                        setActiveMenu(null); // 서브 메뉴 클릭 시 드롭다운 닫기
                      }}
                      style={{
                        padding: "10px 20px",
                        cursor: "pointer",
                        textAlign: "center",
                        backgroundColor: "#f9f9f9",
                      }}
                    >
                      {subMenu.title}
                    </div>
                  ))}
              </div>
            ))}
          </div>
        );
      }
      
  • src/navigaton/RootNavigation.tsx 를 추가시켜줍니다.

    • 적용한 react-router-dom을 이용하여 Route를 쌓아줍니다
    • path는 저희가 설정한 뒤, 해당 path를 이용하면 저희가 원하는 페이지(element)로 이동할 수 있어요
    • import { Route, Routes } from "react-router-dom";
      import { MainScreen } from "../screens";
      import PostListScreen from "../screens/PostListScreen";
      
      export default function RootNavigation() {
        return (
          <>
            <Routes>
              <Route path="/" element={<MainScreen />} />
              <Route path="/post/list/:subMenuNo" element={<PostListScreen />} />
            </Routes>
          </>
        );
      }
      
  • src/navigation/index.ts 도 추가시켜줍니다.

    • 이전에 포스팅했던 배럴 파일 패턴으로 적용해줍니다.
    • export { default as RootNavigation } from "./RootNavigation";
      
  • MainComponent.tsx

    • 위쪽에서 만든 RootNavigation을 적용해줍니다.
    • MainComponent 내부에 RootNavigation을 넣은 이유는
    • Top, Left, Right에 위치한 Component들은 고정하고, 중앙 영역에 있는 내용만 바뀌게 하기 위함입니다.
    • import { RootNavigation } from "../navigator";
      
      export default function MainComponent() {
        return (
          <div className="main-component" style={{ width: "100%" }}>
            <RootNavigation />
          </div>
        );
      }
      
  • SidebarLeft.tsx

    • 이제 router 연동을 끝냈으니 메뉴 버튼을 클릭하면 이동시키는 작업을 해보겠습니다.
    • import { useState } from "react";
      import { useNavigate } from "react-router-dom";
      
      export default function SidebarLeft() {
        const navigation = useNavigate();
        const [activeMenu, setActiveMenu] = useState<number | null>(null); // 드롭다운 상태 관리
        const menus = [
          {
            no: 1,
            title: "React Menu 1",
            subMenus: [
              {
                no: 1,
                title: "React SubMenu 1-1",
              },
              {
                no: 3,
                title: "React SubMenu 1-2",
              },
            ],
          },
          {
            no: 2,
            title: "React Menu 2",
            subMenus: [
              {
                no: 2,
                title: "React SubMenu 2-1",
              },
              {
                no: 4,
                title: "React SubMenu 2-2",
              },
            ],
          },
        ];
      
        const toggleDropdown = (menuNo: number) => {
          setActiveMenu((prev) => (prev === menuNo ? null : menuNo)); // 클릭 시 드롭다운 토글
        };
      
        return (
          <div
            style={{
              alignItems: "center",
              justifyContent: "center",
              display: "flex",
              flexDirection: "column",
              width: "20%",
            }}
          >
            <div style={{ margin: 10, fontWeight: 900, fontSize: 17 }}>MENU</div>
            {menus?.map((menu) => (
              <div key={menu.no} style={{ width: "100%" }}>
                <div
                  onClick={() => toggleDropdown(menu.no)}
                  style={{
                    padding: "10px",
                    cursor: "pointer",
                    textAlign: "center",
                    backgroundColor:
                      activeMenu === menu.no ? "#f0f0f0" : "transparent", // 드롭다운 활성화 시 배경색 변경
                  }}
                >
                  {menu.title}
                </div>
                {activeMenu === menu.no &&
                  menu.subMenus?.map((subMenu) => (
                    <div
                      key={subMenu.no}
                      onClick={() => {
                        navigation(`/post/list/${subMenu.no}`);
                        setActiveMenu(null); // 서브 메뉴 클릭 시 드롭다운 닫기
                      }}
                      style={{
                        padding: "10px 20px",
                        cursor: "pointer",
                        textAlign: "center",
                        backgroundColor: "#f9f9f9",
                      }}
                    >
                      {subMenu.title}
                    </div>
                  ))}
              </div>
            ))}
          </div>
        );
      }
      
  • 적용하고 메뉴를 눌러보세요

  • 바뀌시는게 보이나요~?

7. 마무리 및 참고