react-router-dom
SPA(Single Page Application)의 경우 페이지 렌더링에 필요한 모든 자바스크립트가 초기에 브라우저에게 전달되기 때문에 페이지(혹은 탭) 간 이동 시 원칙적으로는 서버에 재요청을 할 필요가 없습니다. 따라서 SPA에서 다른 페이지로 이동할 때에 <a> 태그를 사용하는 것은 서버에 불필요한 요청을 보내는 안티패턴에 해당됩니다.
react-router-dom을 사용하면 이런 상황에서 브라우저에 이미 로드되어 있는 코드들을 활용하여 페이지간 이동을 구현할 수 있습니다. react-router-dom은 리액트에서 SPA를 구현할 때 가장 많이 쓰이는 패키지 중 하나입니다.
기본적으로 웹페이지는 내부에서 URL을 요청할 때 페이지 전체를 서버에 재요청한 후 새로고침합니다.
https://codingbroker.com/about => https://codingbroker.com/tutorial
이는 화면의 모든 부분을 다시 로드해야 하기 때문에 로딩시간이 지연된다는 단점이 있습니다.
SPA(Single Page Application)는 페이지 이동 시 화면의 header나 footer, sidebar 등 다시 로드해도 변함이 없는 부분들은 그대로 유지한 채 변경되는 부분의 데이터만 가져와서 페이지를 로드합니다. 리액트로 SPA를 구현한다는 것은 곧 해당 요청에 맞는 컴포넌트만 라우팅 하여 부분적으로 렌더링 한다는 것을 의미합니다.
이때 요청에 맞는 컴포넌트를 매칭시키기 위해 react-router-dom을 사용합니다.
참고)
react-router와 react-router-dom, react-router-native의 차이
react-router-dom은 웹에서 쓰이는 컴포넌트를, react-router-native는 react-native를 활용한 앱개발에 쓰이는 컴포넌트를 포함하고 있으며 react-router는 이 둘을 합친 패키지입니다. 웹개발을 위해서는 react-router-dom만 설치하면 됩니다.
예시
먼저 create-react-app을 통해 프로젝트를 생성하고 react-router-dom을 설치합니다.
npx create-react-app my-app
cd my-app
npm install react-router-dom
초기화면입니다.
import React from "react";
function App() {
return (
<div className="App">
<h1>App</h1>
</div>
);
}
export default App;
react-router-dom에서 BrowserRouter, Route, Switch, Link를 불러옵니다.
import React from "react";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
function App() {
return (
<div className="App">
<h1>App</h1>
</div>
);
}
export default App;
BrowserRouter
react-router-dom의 라우터는 <BrowserRouter>와 <HashRouter> 두 가지가 있습니다. BrowserRouter는 HTML5의 history API를 활용하여 UI를 업데이트하고, HashRouter는 URL의 hash를 활용한 라우터입니다. HashRouter는 정적인(static) 페이지에 적합합니다. request와 response로 이루어지는 동적인 페이지를 제작할 때에는 BrowserRouter가 보편적으로 쓰입니다. 이 글에서도 BrowserRouter를 사용하겠습니다.
Route
요청받은 pathname에 해당하는 컴포넌트를 렌더링 합니다.
Switch
path의 충돌이 일어나지 않게 Route들을 관리합니다. Swtich 내부에 Route들을 넣으면 요청에 의해 매칭되는 Route들이 다수 있을 때 제일 처음 매칭되는 Route만 선별하여 실행합니다. 따라서 url 겹침이나 충돌 오류를 방지할 수 있습니다. path와 매칭되는 Route가 없을 때에는 맨 밑에 default Route의 실행이 보장됩니다.(path 속성을 설정하지 않은 Route)
Link
링크를 생성합니다. a태그가 아닌 이 컴포넌트를 사용해야 합니다.
BrowserRouter를 불러온 후 Link를 추가합니다.
// ... 생략
function App() {
return (
<div className="App">
<h1>App</h1>
<BrowserRouter>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/beauty">Beauty</Link>
</li>
<li>
<Link to="/game">Game</Link>
</li>
</ul>
</BrowserRouter>
</div>
);
}
// 생략 ...
ul과 li 태그를 사용해서 3개의 Link를 만들었습니다. Link를 클릭하면 to 속성으로 설정한 pathname으로 요청을 보내게 됩니다. 아래 gif에서 클릭했을 때의 url 변화를 확인해 보시길 바랍니다.
주소창을 통해 요청이 보내지는 것이 보이시나요?
다음으로 Route를 사용해서 이 요청을 받아야 합니다. Switch와 그 안에 Route 3개를 추가합니다.
// ... 생략
function App() {
return (
<div className="App">
<h1>App</h1>
<BrowserRouter>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/beauty">Beauty</Link>
</li>
<li>
<Link to="/game">Game</Link>
</li>
</ul>
<Switch>
<Route path="/"></Route>
<Route path="/beauty"></Route>
<Route path="/game"></Route>
</Switch>
</BrowserRouter>
</div>
);
}
// 생략 ...
Link를 통해 생성된 요청을 Route가 받게 되었습니다. Link의 to 속성과 동일한 path를 가지고 있는 Route가 매칭됩니다.
Route가 요청을 받았다면 각 요청에 맞는 응답을 해줘야 합니다. 렌더링 할 컴포넌트를 생성하고 Route가 응답할 컴포넌트를 component 속성에 설정하겠습니다.
import React from "react";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
function App() {
return (
<div className="App">
<h1>App</h1>
<BrowserRouter>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/beauty">Beauty</Link>
</li>
<li>
<Link to="/game">Game</Link>
</li>
</ul>
<Switch>
<Route path="/" component={Home}></Route>
<Route path="/beauty" component={Beauty}></Route>
<Route path="/game" component={Game}></Route>
</Switch>
</BrowserRouter>
</div>
);
}
function Home() {
return <div>Home component</div>;
}
function Beauty() {
return <div>Beauty component</div>;
}
function Game() {
return <div>Game component</div>;
}
export default App;
Home, Beauty, Game 컴포넌트를 생성하고 Route의 component 속성으로 전달하였습니다. 이제 매칭된 Route의 컴포넌트가 렌더링 됩니다.
그런데 문제가 있습니다. 먼저 실행화면을 보시죠.
클릭할 때마다 각각 / , /beauty, /game에 해당되는 요청이 수행되고 있지만, 매칭되는 Route의 component를 렌더링 하지 않고 항상 Home 컴포넌트만 렌더링 하고 있습니다.
원인은 Route의 순서가 잘못되었기 때문입니다.
<Route path="/" component={Home}></Route>
<Route path="/beauty" component={Beauty}></Route>
<Route path="/game" component={Game}></Route>
Switch는 매칭되는 제일 첫 번째의 Route를 선택하는데, 첫 번째 Route의 path에 전달한 "/"는 모든 요청에 매칭됩니다. 따라서 항상 Home 컴포넌트만 렌더링 된 것이죠. 이 때는 순서를 변경하거나, Route에 exact 속성을 추가하면 됩니다. exact 속성을 추가하면 pathname과 정확히 일치하는 때에만 해당 Route를 매칭시킵니다.
첫 번째 Route에 exact 속성을 추가합니다.
<Route path="/" exact component={Home}></Route>
<Route path="/beauty" component={Beauty}></Route>
<Route path="/game" component={Game}></Route>
이제 요청에 맞는 컴포넌트들이 렌더링 됩니다.
수고하셨습니다!
'리액트' 카테고리의 다른 글
[react] 불필요한 리렌더링 방지하기 - React.memo 성능 최적화 (1) | 2019.12.23 |
---|---|
[react] 올바른 리액트 조건부 렌더링 (if else 구문과 ternary operator, && 차이) (1) | 2019.09.09 |
[react] 예제로 따라하는 리액트 훅(hook) - useEffect (3) | 2019.08.03 |
[react] 예제로 따라하는 리액트 훅(hook) - useState (3) | 2019.08.02 |
[react] 리액트 훅(react hook)이란? - 클래스형 컴포넌트와 비교 (3) | 2019.07.30 |