안녕하세요, 오늘은 리액트 라우터에 대해 알아보았습니다. 라우터란 무엇인지, 어떻게 사용하는지 정리했습니다.
웹 브라우저에 여러 컴포넌트가 있을 때, 특정 조건에 맞춰서 필요로 하는 것만 보이도록 조절한다. (Restful API)

프로젝트 생성 및 라이브러리 설치
c:\react> npx create-react-app router-app
c:\react> cd router-app
c:\react\router-app> npm install react@18 react-dom@18
c:\react\router-app> npm install web-vitals
c:\react\router-app> npm install react-router-dom
c:\react\router-app> code .
c:\react\router-app> npm start
import {BrowserRouter, Router, Route } from "react-router-dom";
function App(){
return(
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />}/>
<Route path="/about" element={<About />}/>
<Route path="/contact" element={<Contact />}/>
</Routes>
</BrowserRouter>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
loader: async () => {
const data = await fetchDataFromHome();
return data;
},
errorElement: <ErrorPage />
},
{
path: "/About",
element: <About />,
},
{
path: "/Contact",
element: <Contact />,
}
]);
function App(){
return <RouterProvider router={router } />
}
export default function Home() {
return (
<div>
<h1>Home</h1>
<h2>가장 먼저 보이는 페이지</h2>
</div>
);
}
export default function About() { // export defaulta -> export default
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
</div>
);
}
App.js 파일에 react-router-dom에 내장되어 있는 BrowserRouter 컴포넌트를 추가.
Route 컴포넌트로 특정 경로에 원하는 컴포넌트 보여주기 (주소 패턴에 따라 다른 컴포넌트를 제공)

import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/info">Info</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
</Routes>
</BrowserRouter>
);
}
export default App;
http://localhost:3000/ ⇒ Home 컴포넌트가 제공
http://localhost:3000/about ⇒ About 컴포넌트가 제공
클릭하면 다른 주소로 이동시켜주는 컴포넌트. (a 태그와 같은 기능을 하지만 a는 서버에 리퀘스트 발생시킨다(서브밋 발생 == 렌더링 발생 -> 상태변수 초기화 되어버림 -> 리액트에서 쓰지 말자!)
HTML5 Histroy API를 사용해 브라우저의 주소만 바꿀 뿐 페이지를 새로 불러오지는 않음.
\링크 이름\
<BrowserRouter>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
<li><Link to="/info">정보</Link></li>
<Route path="/info" element={<About />} />
파라미터 (parameter)
/profile/honggildong
쿼리 문자열 (query String)
/profile?name=honggildong
⇒ 파라미터로 전달된 사용자 식별자와 일치하는 사람의 정보를 출력
import { useParams } from "react-router-dom";
const users = {
mrgo: {
name: "고길동",
desc: "둘리를 싫어하는 자"
},
mrhong: {
name: "홍길동",
desc: "호부호형을 원하는 자"
}
};
// http://localhost:3000/profile/mrgo
// ~~~~
// userid 변수 이름으로 전달
// 주소에 포함된 사용자 식별자(여기에서는 userid)에 해당하는 사용자 정보를 출력
export default function Profile() {
// 주소에 포함된 파라미터를 추출
const params = useParams();
// 파라미터에서 userid의 값을 추출해서
// 해당 값을 이용해서 users 객체에서 일치하는 사용자 정보를 추출
const profile = users[params.userid];
return (
<>
{
profile ? (
<>
<h1>{profile.name}</h1>
<h2>{profile.desc}</h2>
</>
) : (
<h1>일치하는 사용자가 없습니다.</h1>
)
}
</>
);
}
Profile 컴포넌트를 호출하는 링크와 Route를 추가
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Profile from "./Profile";
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/profile/mrgo">고길동 프로파일</Link></li>
<li><Link to="/profile/mrhong">홍길동 프로파일</Link></li>
<li><Link to="/profile/none">없는 프로파일</Link></li>
</ul>
<Routes>
<Route path="/profile/:userid" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
export default App;
...?query_name=query_value?query_name2=query_value2 ~~~ 의 형태로 전달
location 객체를 반환한다.
location.search 값 중 detail 값이 true인 경우 추가 정보가 출력되도록 수정.
쿼리스트링으로 전달되는 모든 값은 문자열 타입을 가진다.



import { useLocation } from "react-router-dom";
import qs from 'qs';
export default function About() {
// 쿼리 스트링을 추출 ==> ?aaaa=aaaa&bbbb=bbbb&cccc=cccc
const location = useLocation();
const quries = qs.parse(location.search, { ignoreQueryPrefix: true });
console.log(quries);
// http://localhost:3000/about?detail=true&name=hong&age=23 형태로 요청을 하면
// {detail: 'true', name: 'hong', age: '23'}
// ~~~~~~ ~~~~~~ ~~~~ <== 쿼리 스트링으로 전달되는 모든 값은 문자열 타입을 가짐
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
{
quries.detail === "true" && <h2>상세 내역입니다.</h2>
}
</div>
);
}

쿼리 스트링 파싱을 도와주는 라이브러리
c:\react\router-app> npm install qs
사용하는 방법

useSearchParams 훅을 사용하도록 About 컴포넌트를 수정.
파싱하는 과정이 필요 X.
import { useSearchParams } from "react-router-dom";
export default function About() {
/*
const location = useLocation();
const quries = qs.parse(location.search, { ignoreQueryPrefix: true });
*/
const [searchParams, setSearchParams] = useSearchParams();
const detail = searchParams.get("detail");
return (
<div>
<h1>About</h1>
<h2>리액트 라우트 연습</h2>
{
detail === "true" && <h2>상세 내역입니다.</h2>
}
</div>
);
}
복잡한 애플리케이션의 URL과 화면 계층을 효과적으로 관리.
Outlet과 childern 속성을 활용해 계층 구조를 반영한 라우팅을 설정.
특정 라우트의 하위 경로로 구성된 라우트
부모 라우트의 레이아웃을 공유하면서 자식 컴포넌트를 렌더링
프로파일 컴포넌트로의 링크(Link)와 라우팅 결과를 출력할 Outlet을 포함하는 컴포넌트.
Outlet: Route의 children으로 들어오는 JSX 엘리멘트를 보여주는 역할
import { Link, Outlet } from "react-router-dom";
export default function Profiles() {
return (
<>
<h1>사용자 목록</h1>
<ul>
<li><Link to="/profiles/mrgo">고길동 프로파일</Link></li>
<li><Link to="/profiles/mrhong">홍길동 프로파일</Link></li>
<li><Link to="/profiles/none">없는 프로파일</Link></li>
</ul>
<hr />
<Outlet />
</>
);
}
Profile 컴포넌트와 관련된 Link와 Route를 제거하고, Profiles 컴포넌트와 관련된 Link와 Route를 추가.
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
{/*
<li><Link to="/profile/mrgo">고길동 프로파일</Link></li>
<li><Link to="/profile/mrhong">홍길동 프로파일</Link></li>
<li><Link to="/profile/none">없는 프로파일</Link></li>
*/}
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
{/*
<Route path="/profile/:userid" element={<Profile />} />
*/}
<Route path="/profiles" element={<Profiles />}>
<Route path=":userid" element={<Profile />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
중첩된 라우트와 Outlet 컴포넌트를 이용해 각 페이지(컴포넌트)에서 공통적으로 보여줘야 하는 레이아웃을 처리할 때 유용.
메뉴를 \
태그를 포함하고, 각 메뉴를 클릭했을 때 나타낼 컴포넌트는 \ 태그에 출력.메뉴를 제거하고, Layout 컴포넌트로 각 페이지 컴포넌트를 둘러싼다.
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />} >
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
<Route path="/profiles" element={<Profiles />}>
<Route path=":userid" element={<Profile />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
header{
background-color: #ccc;
padding: 16px;
font-size: 32px;
}
ul{
list-style-type: none;
margin: 0;
padding: 0;
}
li{
display: inline;
padding: 0 20px;
}
Link 컴포넌트를 사용하지 않고 페이지를 이동할 때 사용하는 훅 함수
const navigate = useNavigate();
이전 페이지로 이동 버튼, 정보 페이지로 이동 버튼 추가
import { Link, Outlet, useNavigate } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
);
}
링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트.
이 컴포넌트를 사용하면 style 또는 className을 설정할 때 { isActive: boolean }을 매개변수로 전달받는 함수를 정의할 수 있음.
소개 메뉴를 클릭하면 글자색을 붉은색으로 설정

import { Outlet, Link, useNavigate, NavLink } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><NavLink to="/about" style={
({ isActive }) => isActive ? { color: "red" } : undefined
}>소개</NavLink></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
);
};
Route 컴포넌트의 path props의 값으로 *를 사용하면 아무 텍스트나 매칭한다는 뜻이 되며, 라우트 엘리먼트의 상단에 위치하는 라우트의 규칙을 모두 확인하고, 일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 됨.
export default () => {
return (
<div style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 64,
position: "absolute",
width: "100%",
height: "100%",
}}>404 Not Found</div>
);
}
NotFound 컴포넌트를 라우트로 등록
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
import NotFound from "./NotFound";
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />} >
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
<Route path="/profiles" element={<Profiles />}>
<Route path=":userid" element={<Profile />} />
</Route>
</Route>
<Route path="*" element={<NotFound/>} />
</Routes>
</BrowserRouter>
);
}
export default App;
컴포넌트를 화면에 보여주는 순간 다른 컴포넌트(페이지)로 이동할 때 사용.
페이지를 리다이렉트할 때 사용.
export default () => <h1>로그인 페이지</h1>;
import { Navigate } from "react-router-dom";
export default () => {
if(!isLoggedIn){
return <Navigate to = "/login" replace={true}/>
}
return <h1>마이 페이지 </h1>;
}
위의 라우트 정보 추가.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
import NotFound from "./NotFound";
import MyPage from "./MyPage";
import Login from "./Login";
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/info" element={<About />} />
<Route path="/profiles" element={<Profiles />}>
<Route path=":userid" element={<Profile />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="/mypage" element={<MyPage />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
export default App;
마이페이지 메뉴 추가
import { Outlet, Link, useNavigate, NavLink } from "react-router-dom";
import "./Layout.css";
export default function Layout() {
const navigate = useNavigate();
return (
<div>
<header>
<ul>
<li><Link to="/">홈</Link></li>
<li><NavLink to="/about" style={
({ isActive }) => isActive ? { color: "red" } : undefined
}>소개</NavLink></li>
<li><Link to="/info">정보</Link></li>
<li><Link to="/profiles">프로파일</Link></li>
<li><Link to="/mypage">마이페이지</Link></li>
</ul>
<button onClick={() => navigate(-1)}>이전 페이지로 이동</button>
<button onClick={() => navigate("/info")}>정보 페이지로 이동</button>
</header>
<main>
<Outlet />
</main>
</div>
);
};
마이페이지 메뉴를 클릭하면 로그인 페이지로 이동하는 것을 확인

일반적으로 replace = { true }로 둔다. (계속해서 같은 페이지가 보여지는 걸 방지)
RouterProvider 추가, createBrowserRouter 함수에 인자 값으로 라우팅 정보를 전달
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Profiles from "./Profiles";
import Profile from "./Profile";
import Layout from "./Layout";
import NotFound from "./NotFound";
import MyPage from "./MyPage";
import Login from "./Login";
const router = createBrowserRouter([
{
path: "/", element: <Layout />, children: [
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
{ path: "/info", element: <About /> },
{
path: "/profiles", element: <Profiles />, children: [
{ path: ":userid", element: <Profile /> }
]
},
{ path: "/login", element: <Login /> },
{ path: "/mypage", element: <MyPage /> }
]
},
{ path: "*", element: <NotFound /> }
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;