JavaScript/React
React - React Router DOM
제이널
2022. 1. 26. 11:45
이 글은 react-router-dom 6 버전을 기준으로 작성되었습니다.
1. React Router?
- React를 위한 클라이언트 측 라우팅 라이브러리입니다.
- URL이 바뀌면 페이지를 새로고침하지 않고, 필요한 부분만 로드합니다.
- React는 기본적으로 UI 구축에만 초점을 맞추기 때문에, 라우팅을 위한 내장 프로그램이 없습니다.
- create-react-app 구성에 기본적으로 포함되어 있지 않기 때문에, 별도로 설치해야 합니다.
- https://reactrouter.com/docs/en/v6/getting-started/concepts#main-concepts
React Router vs. React Router DOM
- react-router는 핵심 컴포넌트와 기능을 export 합니다.
- react-router-dom은 react-router를 포함하는 확장 패키지로써, react-router를 웹의 DOM으로 바인딩합니다.
- react-router-dom에는 react-router가 포함되어 있으므로, 둘 다 설치할 필요는 없습니다.
- https://stackoverflow.com/questions/42684809/react-router-vs-react-router-dom-when-to-use-one-or-the-other
2. react-router-dom 최신 버전(v6) 설치
$ npx create-react-app react-router-dom-test
$ cd react-router-dom-test
$ npm i react-router-dom
3. 사용해보기
App.js 수정
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
return <div>Home 페이지</div>;
}
function Profile() {
return <div>Profile 페이지</div>;
}
function About() {
return <div>About 페이지</div>;
}
export default App;
- BrowserRouter : HTML5 환경에서 라우터를 구현할 수 있게 해주는 API 컴포넌트(HTML5를 지원하지 않는 구형 브라우저에서 동작하지 않음)
- Routes : v6에서 새로 추가된 <Switch> 대체 컴포넌트
- Route : 현재 URL 경로가 path와 일치할 때 element를 렌더링
개발 서버 시작 후 접속 테스트
$ PORT=8080 npm start
4. Dynamic 라우팅
- URL을 동적으로 매핑시키는 라우팅 방법입니다.
/* App.js */
import { BrowserRouter, Route, Routes, Outlet, useParams } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />}>
<Route path=":id" element={<Profile />} />
<Route path=":id/:name" element={<Profile />} />
</Route>
<Route path="/*" element={<Error />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
return <div>Home 페이지입니다.</div>;
}
function Profile() {
const parm = useParams();
return (
<>
<div>Profile 페이지입니다.</div>
<div>id : { parm.id }</div>
<div>name : { parm.name }</div>
{/* <Outlet /> */}
</>
)
}
function Error() {
return <div>Error 페이지입니다.</div>;
}
export default App;
- /profile : 부모 Route의 element 컴포넌트를 렌더링
- /profie/123 : 자식 Route 중, :id와 매핑시켜서, 부모 Route의 element 컴포넌트를 렌더링하고 파라미터 전달
- /profile/123/123 : 자식 Route 중, :id, :name과 매핑시켜서, 부모 Route의 element 컴포넌트를 렌더링하고 파라미터 전달
- /profile/123/123?age=1 : react-router-dom의 useLocation()과 query-string의 queryString.parse()를 사용해 Query String을 캐치 가능
- /profile/123/123/123 : 매핑되는 URL이 없으므로, path="/*"를 가지는 Route의 컴포넌트를 렌더링
- <Outlet /> : 부모 Route 컴포넌트를 렌더링하고, 매핑된 자식 Route 컴포넌트도 함께 렌더링
<Outlet /> 적용
5. Dynamic 라우팅 - Link
- React는 SPA로 동작하기 때문에 <a> 태그를 사용하면 페이지를 새로 로드해서 다시 렌더링 하는 특징이 있습니다.
- react-router-dom의 Link 컴포넌트를 사용하면, HTML5 History API를 사용해 URL만 바꾸고, 페이지를 새로 불러오지 않습니다.
/* App.js */
import { ..., Link } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/profile">Profile</Link></li>
<li><Link to="/profile/123/123">Profile/123/123</Link></li>
<li><Link to="/erroroccured">Error Page</Link></li>
</ul>
<Routes>
...
</Routes>
</BrowserRouter>
);
}
...
export default App;
- Link 컴포넌트는 <a> 태그의 href 대신 to를 사용해 이동할 URL을 정의할 수 있습니다.
6. Dynamic 라우팅 - NavLink
- Link 컴포넌트와 비슷하지만, 현재 활성화된 NavLink에 스타일 또는 클래스 속성을 토글해 줄 수 있습니다.
- NavLink의 두 속성이 활성화 되려면, 현재 브라우저의 URL과 to 속성의 URL이 일치해야 합니다.
/* App.js */
import { ..., NavLink } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<ul>
<li><NavLink to="/" style={ ({ isActive }) => isActive ? {color:'red'}:undefined }>Home</NavLink></li>
<li><NavLink to="/profile" className={ ({ isActive }) => isActive ? 'active':''}>Profile</NavLink></li>
<li>
<NavLink to="/profile/123/123">
{({ isActive }) => (
<span style={ isActive ? {color:"green"}:undefined}>Profile/123/123</span>
)}
</NavLink>
</li>
...
</ul>
<Routes>
...
</Routes>
</BrowserRouter>
);
}
...
export default App;
- v5에서 사용하던 activeStyle과 activeClassName은 없어졌고, v6에선 함수 형태로 정의할 수 있습니다.
- isActive 값이 들어있는 객체를 인자로 구조분해할당 받아서, 현재 NavLink가 활성화 되었는지 확인할 수 있습니다.
- 컴포넌트의 내용을 함수로 작성할 땐, 함수와 형제 요소가 없어야 합니다.
7. JS로 라우팅 이동하기
- JS에서 useNavigate 훅을 사용해 URL을 이동시킬 수 있습니다.
/* App.js */
import ... from 'react-router-dom';
import Login from './pages/Login';
function App() {
return (
<BrowserRouter>
...
<Routes>
<Route path="/login" element={<Login />} />
...
</Routes>
</BrowserRouter>
);
}
...
export default App;
/* Login.jsx */
import { useNavigate } from 'react-router-dom';
export default function Login() {
const navigate = useNavigate();
function login() {
setTimeout(()=>{
navigate('/');
}, 1000);
}
return (
<div>
<h2>Login 페이지입니다.</h2>
<button onClick={login}>로그인하기</button>
</div>
)
}
- useNavigate : v5에서 사용하던 props.history 대체 훅입니다. Programmatically 하게 URL을 이동시킬 수 있게 해줍니다.
- navigate('/') : v5에서 사용하던 props.history.push의 대체입니다. 인자로 Route에 등록한 path로 이동시킵니다.
8. Navigate
- URL을 Route 경로로 redirect 해주는 컴포넌트입니다.
/* App.js */
import { ..., Navigate } from "react-router-dom";
const isLogin = false;
function App() {
return (
<BrowserRouter>
...
<Routes>
...
<Route path="/login" element={<Login />} />
<Route path="/profile" element={isLogin ? <Profile />:<Navigate to="/login"/>} >
...
</Route>
...
</Routes>
</BrowserRouter>
);
}
...
function Login() {
return (
<div>
<h2>Login 페이지입니다.</h2>
</div>
)
}
export default App;
- 로그인이 되지 않은 상태에서 프로필 화면에 접근하려 하면 login 페이지로 redirect 시켜주고 있습니다.
- 삼항 연산자 부분에서 <Navigate> 대신, <Login />을 사용해도 되지만,Login 컴포넌트 수정되었을 때, 모두를 바꿔줘야 하는 측면에선 redirect가 유리하다고 생각합니다.