react-router-dom v6을 기준으로 작성되었습니다.
# npm 사용
npm install react-router-dom
# yarn 사용
yarn add react-router-dom
src/index.js
파일에서 react-router-dom
에 내장되어 있는 BrowserRouter
라는 컴포넌트를 사용하여 감싸면 됨src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
Home
페이지 컴포넌트와 웹 사이트를 소개하는 About
페이지 컴포넌트를 만들어보자.src/pages/Home.jsx
import React from 'react'
function Home() {
return (
<div>
<h1>Home</h1>
<p>가장 먼저 보여지는 페이지</p>
</div>
)
}
export default Home
src/pages/About.jsx
import React from 'react'
function About() {
return (
<div>
<h1>About</h1>
<p>리액트 라우터를 사용해보는 프로젝트 입니다.</p>
</div>
)
}
export default About
Route
컴포넌트를 통해 라우트 설정<Route path="주소규칙" element={보여줄 컴포넌트 JSX} />
Route
컴포넌트는 Routes
컴포넌트 내부에서 사용되어야 함App
컴포넌트를 다음과 같이 Route
컴포넌트를 사용하여 라우트 설정해보자.import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
);
};
export default App;
Link
컴포넌트 사용<Link to="경로">링크 이름</Link>
Home
페이지에서 About
페이지로 이동할 수 있도록 Link
컴포넌트를 Home
페이지 컴포넌트에서 사용해보자.src/pages/Home.js
import { Link } from 'react-router-dom';
import React from 'react'
function Home() {
return (
<div>
<h1>Home</h1>
<p>가장 먼저 보여지는 페이지</p>
<Link to="/about">About</Link>
</div>
)
}
export default Home
useParams
라는 Hook을 사용하여 객체 형태로 조회할 수 있음Route
컴포넌트의 path
props를 통하여 설정Profile
컴포넌트에서는 username
URL 파라미터를 통하여 프로필을 조회한 뒤에 프로필이 존재하지 않으면 ‘존재하지 않는 프로필입니다.’ 라는 문구를 보여주고 존재한다면 프로필 정보를 보여주도록 로직을 작성src/pages/Profile.js
import { useParams } from 'react-router-dom';
const data = {
mjieun: {
name: '문지은',
description: '리액트를 공부하고 있어요.',
},
gildong: {
name: '홍길동',
description: '고전 소설 홍길동전의 주인공',
},
};
function Profile() {
const params = useParams();
const profile = data[params.username];
return (
<div>
<h1>사용자 프로필</h1>
{profile ? (
<div>
<h2>{profile.name}</h2>
<p>{profile.description}</p>
</div>
) : (
<p>존재하지 않는 프로필입니다.</p>
)}
</div>
)
}
export default Profile
App
컴포넌트 파일에 다음과 같이 새로운 라우트를 설정/profiles/:username
과 같이 경로에 :
를 사용하여 설정/profiles/:username/:field
와 같은 형태로 설정할 수 있음import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
</Routes>
);
};
export default App;
Profile
페이지로 이동할 수 있도록 Home
페이지에 Link
추가하기ul
태그를 사용하여 리스트 형태로 작성import { Link } from 'react-router-dom';
import React from 'react'
function Home() {
return (
<div>
<h1>Home</h1>
<p>가장 먼저 보여지는 페이지</p>
<ul>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/profiles/mjieun">mjieun의 프로필</Link>
</li>
<li>
<Link to="/profiles/gildong">gildong의 프로필</Link>
</li>
<li>
<Link to="/profiles/void">존재하지 않는 프로필</Link>
</li>
</ul>
</div>
)
}
export default Home
Profile
페이지로 이동할 수 있음useLocation
이라는 Hook을 사용하여 쿼리스트링 사용useLocation
은 location
객체를 반환하며 이 객체는 현재 사용자가 보고 있는 페이지의 정보를 지니고 있음location.search
값을 통해 조회할 수 있다.About
페이지 컴포넌트를 수정해보고 주소 창에 http://localhost:3000/about?detail=true&mode=1
라고 직접 입력해서 어떤 값이 나타나는지 확인해보자.import React from 'react'
import { useLocation } from 'react-router-dom';
function About() {
const location = useLocation();
return (
<div>
<h1>About</h1>
<p>리액트를 사용해보는 프로젝트 입니다.</p>
<p>쿼리스트링: {location.search}</p>
</div>
)
}
export default About
import React from 'react'
import { useLocation } from 'react-router-dom';
import { useSearchParams } from 'react-router-dom';
function About() {
const [searchParams, setSearchParams] = useSearchParams();
const detail = searchParams.get('detail');
const mode = searchParams.get('mode');
const onToggleDetail = () => {
setSearchParams({ mode, detail: detail === 'true' ? false : true });
};
const onIncreaseMode = () => {
const nextMode = mode === null ? 1 : parseInt(mode) + 1;
setSearchParams({ mode: nextMode, detail });
};
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
<p>detail: {detail}</p>
<p>mode: {mode}</p>
<button onClick={onToggleDetail}>Toggle detail</button>
<button onClick={onIncreaseMode}>mode + 1</button>
</div>
);
}
export default About
src/pages/Articles.jsx
import React from 'react'
import { Link } from 'react-router-dom';
function Articles() {
return (
<ul>
<li>
<Link to="/articles/1">게시글 1</Link>
</li>
<li>
<Link to="/articles/2">게시글 2</Link>
</li>
<li>
<Link to="/articles/3">게시글 3</Link>
</li>
</ul>
)
}
export default Articles
src/pages/Article.jsx
import React from 'react'
import { useParams } from 'react-router-dom';
function Article() {
const { id } = useParams();
return (
<div>
<h2>게시글 {id}</h2>
</div>
);
}
export default Article
src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
<Route path="/articles" element={<Articles />} />
<Route path="/articles/:id" element={<Article />} />
</Routes>
);
};
export default App;
src/pages/Home.jsx
import { Link } from 'react-router-dom';
import React from 'react'
function Home() {
return (
<div>
<h1>Home</h1>
<p>가장 먼저 보여지는 페이지</p>
<ul>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/profiles/mjieun">mjieun의 프로필</Link>
</li>
<li>
<Link to="/profiles/gildong">gildong의 프로필</Link>
</li>
<li>
<Link to="/profiles/void">존재하지 않는 프로필</Link>
</li>
<li>
<Link to="/articles">게시글 목록</Link>
</li>
</ul>
</div>
)
}
export default Home
App
컴포넌트 수정src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
<Route path="/articles" element={<Articles />} />
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />} />
</Route>
</Routes>
);
};
export default App;
Outlet
이라는 컴포넌트를 사용하여 Articles
컴포넌트 수정Route
의 children
으로 들어가는 JSX 엘리먼트를 보여주는 역할을 함Outlet
컴포넌트를 통해 보여짐<Route path=":id" element={<Article />} />
src/pages/Articles.jsx
import React from 'react'
import { Link, Outlet } from 'react-router-dom';
function Articles() {
return (
<div>
<Outlet />
<ul>
<li>
<Link to="/articles/1">게시글 1</Link>
</li>
<li>
<Link to="/articles/2">게시글 2</Link>
</li>
<li>
<Link to="/articles/3">게시글 3</Link>
</li>
</ul>
</div>
)
}
export default Articles
/articles/1
경로로 들어가보면 게시글 하단에 게시글 목록이 나타나는 것을 볼 수 있음Outlet
은 페이지끼리 공통적으로 보여줘야 하는 레이아웃이 있을때도 유용하게 사용할 수 있음Layout
컴포넌트 만들기src/Layout.jsx
import React from 'react'
import { Outlet } from 'react-router-dom';
function Layout() {
return (
<div>
<header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
Header
</header>
<main>
<Outlet />
</main>
</div>
)
}
export default Layout
App
컴포넌트 수정하기src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Layout from './Layout';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />} />
</Route>
</Routes>
);
};
export default App;
Home
페이지에 이동하면 헤더가 잘 나타나는 것을 볼 수 있음path="/"
와 동일한 의미를 가지는 propsHome
컴포넌트가 사용된 Route
컴포넌트를 다음과 같이 변경src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Layout from './Layout';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />} />
</Route>
</Routes>
);
};
export default App;
/
경로로 들어갔을 때 여전히 Home 페이지가 잘 나오는 것을 볼 수 있음Link
컴포넌트를 사용하지 않고 다른 페이지로 이동을 해야 하는 상황에 사용하는 HookLayout
컴포넌트를 다음과 같이 수정해보자.src/Layout.js
import React from 'react'
import { Outlet, useNavigate } from 'react-router-dom';
function Layout() {
const navigate = useNavigate();
const goBack = () => {
// 이전 페이지로 이동
navigate(-1);
};
const goArticles = () => {
// articles 경로로 이동
navigate('/articles');
};
return (
<div>
<header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
<button onClick={goBack}>뒤로가기</button>
<button onClick={goArticles}>게시글 목록</button>
</header>
<main>
<Outlet />
</main>
</div>
);
}
export default Layout
navigate
함수를 사용할 때 파라미터가 숫자 타입이라면 앞으로 가거나, 뒤로 감navigate('/articles', { replace: true });
와 같이 사용<NavLink
style={({isActive}) => isActive ? activeStyle : undefined}
/>
<NavLink
className={({isActive}) => isActive ? 'active' : undefined}
/>
Articles
페이지 컴포넌트에서 이 컴포넌트를 사용해보자.src/pages/Articles.jsx
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom';
const activeStyle = {
color: 'green',
fontSize: 21,
};
function Articles() {
return (
<div>
<Outlet />
<ul>
<li>
<NavLink
to="/articles/1"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 1
</NavLink>
</li>
<li>
<NavLink
to="/articles/2"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 2
</NavLink>
</li>
<li>
<NavLink
to="/articles/3"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 3
</NavLink>
</li>
</ul>
</div>
)
}
export default Articles
import { NavLink, Outlet } from 'react-router-dom';
const Articles = () => {
return (
<div>
<Outlet />
<ul>
<ArticleItem id={1} />
<ArticleItem id={2} />
<ArticleItem id={3} />
</ul>
</div>
);
};
const ArticleItem = ({ id }) => {
const activeStyle = {
color: 'green',
fontSize: 21,
};
return (
<li>
<NavLink
to={`/articles/${id}`}
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 {id}
</NavLink>
</li>
);
};
export default Articles;
src/pages/NotFound.js
import React from 'react'
function NotFound() {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 64,
position: 'absolute',
width: '100%',
height: '100%',
}}
>
404
</div>
)
}
export default NotFound
*
를 사용하여 상단에 위치하는 라우트들의 규칙을 모두 확인하고, 일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 함src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Layout from './Layout';
import NotFound from './pages/NotFound';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
);
};
export default App;
src/pages/Login.jsx
import React from 'react'
function Login() {
return (
<div>로그인 페이지</div>
)
}
export default Login
src/pages/MyPage.jsx
import React from 'react'
import { Navigate } from 'react-router-dom';
function MyPage() {
const isLoggedIn = false;
if (!isLoggedIn) {
return <Navigate to="/login" replace={true} />;
}
return <div>마이 페이지</div>;
}
export default MyPage
src/App.js
import { Route, Routes } from 'react-router-dom';
import About from './pages/About';
import Home from './pages/Home';
import Profile from './pages/Profile';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Layout from './Layout';
import NotFound from './pages/NotFound';
import Login from './pages/Login';
import MyPage from './pages/MyPage';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
};
export default App;
/mypage
경로로 이동하면 페이지가 로딩되는 순간 바로 Login 페이지로 이동되는 것을 볼수 있음실습 전체 코드
References