React 의 라우팅 이해하기
Dynamic 라우팅
Switch 와 NotFound
JSX 링크로 라우팅 이동하기
Redirect
Lead Software Engineer @ProtoPie
Microsoft MVP
TypeScript Korea User Group Organizer
Marktube (Youtube)
이 웅재
react-router-dom
/
/profile
/about
Client (Browser)
Server
/
/profile
/about
Client (Browser)
브라우저에서 최초에 '/' 경로로 요청을 하면,
React Web App 을 내려줍니다.
내려받은 React App 에서 '/' 경로에 맞는 컴포넌트를 보여줍니다.
React App 에서 다른 페이지로 이동하는 동작을 수행하면,
새로운 경로에 맞는 컴포넌트를 보여줍니다.
npm i react-router-dom
cra 에 기본 내장된 패키지가 아닙니다.
react-router-dom 은 Facebook 의 공식 패키지는 아닙니다.
가장 대표적인 라우팅 패키지입니다.
'/' => Home 컴포넌트
'/profile' => Profile 컴포넌트
'/about' => About 컴포넌트
// src/pages/Home.jsx
import React from 'react';
export default function Home() {
return <div>Home 페이지 입니다.</div>;
}
// src/pages/Profile.jsx
import React from 'react';
export default function Profile() {
return <div>Profile 페이지 입니다.</div>;
}
// src/pages/About.jsx
import React from 'react';
export default function About() {
return <div>About 페이지 입니다.</div>;
}
// src/App.js
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Home from './pages/Home';
import Profile from './pages/Profile';
import About from './pages/About';
function App() {
return (
<BrowserRouter>
<Route path="/" component={Home} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
</BrowserRouter>
);
}
export default App;
- Route 컴포넌트에 경로(path) 와 컴포넌트(component) 를 설정하여 나열해줍니다.
- BrowserRouter 로 Route 들을 감싸줍니다.
- 브라우저에서 요청한 경로에 Route 의 path 가 들어있으면 해당 component 를 보여줍니다.
<Route path="/" exact component={Home} />
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
function App() {
return (
<BrowserRouter>
<Route path="/" exact component={Home} />
<Route path="/profile" exact component={Profile} />
<Route path="/profile/:id" component={Profile} />
<Route path="/about" component={About} />
</BrowserRouter>
);
}
export default App;
<Route path="/profile/:id" component={Profile} />
import React from "react";
export default function Profile(props) {
const id = props.match.params.id;
console.log(id, typeof id);
return (
<div>
<h2>Profile 페이지 입니다.</h2>
{id && <p>id 는 {id} 입니다.</p>}
</div>
);
}
props.match.params.id
typeof id => 'string'
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
function App() {
return (
<BrowserRouter>
<Route path="/" exact component={Home} />
<Route path="/profile" exact component={Profile} />
<Route path="/profile/:id" component={Profile} />
<Route path="/about" component={About} />
</BrowserRouter>
);
}
export default App;
<Route path="/about" component={About} />
// src/pages/About.jsx
import React from "react";
export default function About(props) {
const searchParams = new URLSearchParams(props.location.search);
const name = searchParams.get("name");
console.log(name);
return (
<div>
<h2>About 페이지 입니다.</h2>
{name && <p>name 는 {name} 입니다.</p>}
</div>
);
}
new URLSearchParams(props.location.search);
// src/pages/About.jsx
import React from "react";
import queryString from "query-string";
export default function About(props) {
const query = queryString.parse(props.location.search);
const { name } = query;
console.log(name);
return (
<div>
<h2>About 페이지 입니다.</h2>
{name && <p>name 는 {name} 입니다.</p>}
</div>
);
}
const query = queryString.parse(props.location.search);
여러 Route 중 순서대로 먼저 맞는 하나만 보여줍니다.
exact 를 뺄 수 있는 로직을 만들 수 있습니다.
가장 마지막에 어디 path 에도 맞지 않으면 보여지는 컴포넌트를 설정해서,
"Not Found" 페이지를 만들 수 있습니다.
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/profile/:id" component={Profile} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route path="/" component={Home} />
</Switch>
</BrowserRouter>
);
}
export default App;
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/profile/:id" component={Profile} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route path="/" exact component={Home} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
);
}
export default App;
<Link to="/">Home</Link>
<a href="/">Home</a>
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
import Links from "./components/Links";
function App() {
return (
<BrowserRouter>
<Links />
<Switch>
<Route path="/profile/:id" component={Profile} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route path="/" exact component={Home} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
);
}
export default App;
import React from 'react';
import { Link } from 'react-router-dom';
function Links() {
return (
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/profile">Profile</Link>
</li>
<li>
<Link to="/profile/1">Profile/1</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/about?name=mark">About?name=mark</Link>
</li>
</ul>
);
}
export default Links;
import { NavLink } from 'react-router-dom';
activeClassName, activeStyle 처럼 active 상태에 대한 스타일 지정이 가능합니다.
Route 의 path 처럼 동작하기 때문에 exact 가 있습니다.
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import NotFound from "./pages/NotFound";
import Links from "./components/Links";
import NavLinks from "./components/NavLinks";
function App() {
return (
<BrowserRouter>
<Links />
<Switch>
<Route path="/profile/:id" component={Profile} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route path="/" exact component={Home} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
);
}
export default App;
import React from 'react';
import { NavLink } from 'react-router-dom';
const activeStyle = { color: 'green' };
export default function NavLinks() {
return (
<ul>
<li><NavLink exact to="/" activeStyle={activeStyle}>Home</NavLink></li>
<li><NavLink exact to="/profile" activeStyle={activeStyle}>Profile</NavLink></li>
<li><NavLink to="/profile/1" activeStyle={activeStyle}>Profile/1</NavLink></li>
<li>
<NavLink
to="/about"
activeStyle={activeStyle}
isActive={(match, location) => {
if (location.pathname !== '/about') return false;
const searchParams = new URLSearchParams(location.search);
return !searchParams.has('name');
}}
>
About
</NavLink>
</li>
<li>
<NavLink
to="/about?name=mark"
activeStyle={activeStyle}
isActive={(match, location) => {
if (location.pathname !== '/about') return false;
const searchParams = new URLSearchParams(location.search);
return searchParams.has('name');
}}
>
About?name=mark
</NavLink>
</li>
</ul>
);
}
props.history.push("/");
// src/pages/Login.jsx
import React from "react";
export default function Login(props) {
console.log(props);
function login() {
setTimeout(() => {
props.history.push("/");
}, 1000);
}
return (
<div>
<h2>Login 페이지 입니다.</h2>
<button onClick={login}>로그인하기</button>
</div>
);
}
// src/components/LoginButton.jsx
import React from "react";
export default function LoginButton(props) {
console.log(props);
function login() {
setTimeout(() => {
props.history.push("/");
}, 1000);
}
return <button onClick={login}>로그인하기</button>;
}
// src/pages/Login.jsx
import React from "react";
import LoginButton from "../components/LoginButton";
export default function Login(props) {
return (
<div>
<h2>Login 페이지 입니다.</h2>
<LoginButton />
</div>
);
}
import React from "react";
import { withRouter } from "react-router-dom";
function LoginButton(props) {
console.log(props);
function login() {
setTimeout(() => {
props.history.push("/");
}, 1000);
}
return <button onClick={login}>로그인하기</button>;
}
export default withRouter(LoginButton);
export default withRouter(LoginButton);
import { Redirect } from "react-router-dom";
// jsx
<Redirect to="/" />
import React from "react";
import {
BrowserRouter,
Route,
Switch,
Redirect
} from "react-router-dom";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import About from "./pages/About";
import Login from "./pages/Login";
const isLogin = true;
function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/profile/:id" component={Profile} />
<Route path="/profile" component={Profile} />
<Route path="/about" component={About} />
<Route
path="/login"
render={() => (isLogin ? <Redirect to="/" /> : <Login />)}
/>
<Route path="/" component={Home} />
</Switch>
</BrowserRouter>
);
}
export default App;