[요약]
HTML5의 History API를 활용한 라우팅으로 SPA 개발을 도와주는 react 확장 라이브러리
Reference
: 사용자가 요청한 URL에 따라 알맞는 페이지를 보여주는 것
npm install react-router-dom
src>index.js
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
);
src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트를 사용하여 감싸준다.
//router-dom을 적용할 컴포넌트의 최상위 컴포넌트에 래핑해주면 하위 컴포넌트 모두에 적용가능하다.
*BrowserRouter컴포넌트 기능: HTML5의 History API를 사용하여 페이지를 새로 불러오지 않고도
주소를 변경하고, 현재 주소 경로에 관련된 정보를 리액트 컴포넌트에서 사용할 수 있도록 해줌.
특정 경로에서 원하는 컴포넌트를 보여주기
<Route path="/주소규칙" element={보여줄컴포넌트JSX}/>
<Route>
컴포넌트는 반드시 <Routes>
컴포넌트로 감싸주어야한다.<Routes>
<Route path="/" element={<Layout/>}>
<Route path="/" element={<Home/>}> </Route>
<Route path="/About" element={<About/>}> </Route>
<Route path="/profiles/:username" element={<Profile/>}> </Route>
</Route>
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article/>}/>
</Route>
{/* :가 붙은 주소변수는 입력받는 유동값을 의미한다. */}
<Route path="/login" element={<Login/>} />
<Route path="*" element={<NotFound />} />
{/* *는 wildcard문자 , 아무 텍스트나 매칭, 위 모든 path규칙을 확인하고 일치하는 값이
없다면 이 라우트가 화면에 나타난다. */}
</Routes>
컴포넌트 클릭시 to속성으로 설정한 경로로 라우팅시켜준다.
<a href="경로"></a>
: 클릭시 페이지를 새로 불러옴.
<Link to="경로"> 링크이름 </Link>
: 클릭시 페이지를 새로 불러오지 않음.
URL 파라미터
는 주소의 경로에 :username
과 같이 유동적인 값
을 넣는 형태이고,
<Route path="/profiles/:username" element={<Profile/>}> </Route>
src/pages/Profile.js
import { useParams } from 'react-router-dom';
const data = {
jakeshim: {
name: '심현준',
description: 'FE developer',
},
gildong: {
name: '홍길동',
description: '고전 소설 홍길동전의 주인공',
},
};
//data객체 생성
const Profile = () => {
const params = useParams(); //useParams() hook 사용
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;
src/App.js
<Route path="/profiles/:username" element={<Profile/>}> </Route>
라우트 경로에 설정한 :username(설정값)
에 의해
const params =useParams();
값은
주소창 입력시마다 {설정한 값: 입력한값}의 객체를 유동적으로 반환
쿼리 스트링
은 주소의 뒷부분에 ? 문자열 이후에 key=value 로 값을 정의하며 & 로 구분
을 하는 형태.
쿼리스트링을 사용할 때는 URL파라미터와 달리 Route
컴포넌트를 사용할 때 별도로 설정해야되는 것은 없다.
import React from 'react';
import {useLocation} from 'react-router-dom';
const About = () => {
const location = useLocation();
return(
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트임.</p>
<p>쿼리스트링: { location.search }</p>
</div>
);
};
useLocation()훅의 반환값으로써, 사용자가 보고있는 페이지의 정보를 지닌 객체이다.
src/App.js
function App(){
return(
<Routes>
<Route path="/" element={<Layout/>}>
<Route path="/" element={<Home/>}> </Route>
<Route path="/About" element={<About/>}> </Route>
<Route path="/profiles/:username" element={<Profile/>}> </Route>
</Route>
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article/>}/>
</Route>
<Route path="/login" element={<Login/>} />
<Route path="*" element={<NotFound />} />
</Routes>
);
export default App;
중첩된 라우트
의 자식라우트
들은 부모라우트의 요소컴포넌트 안에서
<Outlet/>
컴포넌트로 보여줄 수 있다.
/
경로에서는 Layout
과Layout컴포넌트를 렌더링해서 보여주고
Layout`은 자식라우트의 엘리먼트컴포넌트에서 공통으로 보여진다.
즉, "안에있는걸 바깥(라우트의 컴포넌트)에서 보여준다" 라고 생각하면됨.
src/Layout.js
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
const Layout = () => {
return (
<div>
<header style={{ background: 'lightgray', padding: 16, fontSize: 24 }}>
</header>
<main>
<Outlet />
/*Outlet에 속한 자식라우트로 각각 이동했을 때
부모 라우트의 엘리먼트인 Layout컴포넌트는
각 라우트에서 여전히 지속된다.*/
</main>
</div>
);
};
export default Layout;
https://reactrouter.com/docs/en/v6/getting-started/tutorial [Nested Routes]
src/routes/expenses.jsx
export default function Expenses() {
return (
<main style={{ padding: "1rem 0" }}>
<h2>Expenses</h2>
</main>
);
}
src/routes/invoices.jsx
export default function Invoices() {
return (
<main style={{ padding: "1rem 0" }}>
<h2>Invoices</h2>
</main>
);
}
src/App.js
import { Outlet, Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Bookkeeper</h1>
<nav
style={{
borderBottom: "solid 1px",
paddingBottom: "1rem",
}}
>
<Link to="/invoices">Invoices</Link> |{" "}
<Link to="/expenses">Expenses</Link>
</nav>
<Outlet />
</div>
);
}
index.js
import ReactDOM from "react-dom/client";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="expenses" element={<Expenses />} />
<Route path="invoices" element={<Invoices />} />
</Route>
</Routes>
</BrowserRouter>
);
The parent route (App.js) persists while the
<Outlet>
swaps between the two child routes (<Invoices>
and<Expenses>
)!
<outlet/>
은 자식라우트에서 보여주는 컴포넌트를 추상적으로 의미하고
각 자식 라우트의 경로로 이동했을 때,<App/>
컴포넌트를 렌더링해서 보여주며<outlet/>
의 자리에는 각 자식라우트가 보여주는 컴포넌트가 위치하게 된다.
useNavigate() 반환값을 const navigate 식별자에 할당한다.
다음과같이 이벤트핸들러로 등록해서 사용할 수 있다.
const goArticles = () => {
navigate('/articles', { replace: true });
}
<button onClick={goArticles}> 게시글로 </button>
'/' -> Home -> About -> 게시글목록클릭('/articles'로 이동)
{relace:true}를 2nd parameter 로 주었기 때문에
이동시점의 페이지(About)페이지를 페이지 히스토리에 남기지
않아서 뒤로가기(navigate(-1))시 Home이 나타나게된다.
NavLink 컴포넌트는 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우
특정 스타일 또는 CSS 클래스
를 적용하는 컴포넌트.
이 컴포넌트를 사용시 'style'
또는 'className'
을 설정할 때
{ isActive: boolean }
을 파라미터로 전달받는 함수 타입의 값을 전달함.
<NavLink
style={({isActive}) => isActive ? activeStyle : undefined}
/>
NavLink컴포넌트 활성화시
const {isActive:isActive} = {isActive: true} 객체구조분해할당으로
isActive = true 로 style의 값으로 전달됨.
[추가]
#1.
<NavLink to="url" style={ ({isActive : isActive}) => isActive ? activeStyle :undefined }
//객체 프로퍼티축약
//NavLink 컴포넌트 클릭시 해당 isActive = true가 전달되면서 style={activeStyle}이 적용됨.
//react는 style={ css객체 } 로 전달받는다.
#2.<NavLink className={({isActive}) => isActive ? 'active' : undefined}
src/pages/Articles.js
import { NavLink, Outlet } from 'react-router-dom';
const Articles = () => {
const activeStyle = {
color: 'green',
fontSize: 21,
};
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;
<Route path="*" element={<NotFound/>} />
라우트를 라우트 맨 아랫단에 위치시켜
이 라우트 엘리먼트 상단에 위치하는 라우트들의 규칙을 모두 확인하고
일치하는 라우트가 없다면 이 라우트가 화면에 나타나게 된다.
'*'는 wildcard문자로써 이는 아무 텍스트나 매칭한다는 의미임.
import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import About from './pages/About';
import Article from './pages/Article';
import Articles from './pages/Articles';
import Home from './pages/Home';
import NotFound from './pages/NotFound';
import Profile from './pages/Profile';
const App = () => {
return (
<Routes>
<Route path="/" 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;
컴포넌트를 화면에 보여주는 '순간' 페이지를 리다이렉트하고 싶을 떄 사용한다.
import {navigate} from 'react-router-dom;
const MyPage = ()=>{
const inLoggedIn = false;
if(!isLoggedIn) {
return <Navigate to="/login" replace={true} />;
} /*return문은 그 즉시 값을 리턴하고, 해당 함수의 실행을멈춘다 (if문은 함수가 아니므로
MyPage함수 실행을 즉시 중단하고, return문 이후의 코드를 무시한다.)
return <div>마이페이지</div>;*/
};
export default MyPage;
isLoggedIn값이 false,즉 로그인이 안돼있으면 MyPage컴포넌트는
<Navigate to="/login" replace={true} />