오늘의 주제는 바로 react-router입니다! (react-router@3강좌라서 최신인 react-router@4와는 매우 차이가 있습니다. 조만간 react-router@4로 업데이트하도록 하겠습니다.)
전 시간까지 컴포넌트에 대해 알아봤었죠? 하지만 컴포넌트만 가지고는 웹 페이지를 만들 수 없습니다. 페이지가 하나밖에 없기 때문이죠. 물론, 페이지 깜박임 없이 하나의 페이지만으로 동작하는 싱글 페이지 애플리케이션이 유행이었던 적이 있지만, 주소가 없으므로 특정 페이지에 접속하기 힘들고 북마크를 할 수 없다는 단점이 있었습니다.
angular이나 angular2, backbone같은 프레임워크는 프레임워크이기 때문에 라우팅 기능이 기본적으로 들어있습니다. 하지만 react는 view만 담당하는 라이브러리입니다. 그래서 라우팅을 담당하는 react-router을 따로 설치해주어야 합니다. 그리고 나중에 model과 controller를 담당하는 패키지를 또 설치해야 합니다.
react-router를 사용하면 싱글 페이지 애플리케이션과 같이 페이지 깜박임이 없으면서도 주소를 가질 수 있게 됩니다. 제 홈페이지의 예를 보면 알 수 있습니다. 일반 홈페이지는 페이지를 넘길 때 페이지가 깜박이면서 다음으로 넘어가지만, 제 홈페이지는 레이아웃은 고정되어 있고 내용만 바뀝니다. 주소도 그에 따라 바뀌고요. 이것을 가능케 하는 것이 react-router입니다.
npm install --save react-router
이제 react-router를 이용할 수 있습니다. 이전 시간까지 함께 했던 Basic 컴포넌트는 잊고, 새롭게 시작해봅시다. 여러 개의 컴포넌트를 미리 만들어두어야 합니다. Node.js 강좌 시간에 했던 턴제 게임 만들기 홈페이지를 React 컴포넌트로 바꿔보겠습니다. turn.js도 넣어주세요. react.html은 지금까지 썼던 것을 그대로 쓰면 됩니다.
전체 폴더 구조
components 폴더
--App.js
--About.js
--Index.js
--NoMatch.js
render.js
react.html
turn.js
components/App.js 레이아웃
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class App extends Component {
render() {
return (
<header>턴제 게임</header>
<div className="detail">
{this.props.children}
</div>
<footer>Copyright ZeroCho. All right reserved.</footer>
<script src="./turn.js" />
);
}
}
App.propTypes = {
children: PropTypes.element.isRequired,
};
export default App;
components/About.js 안내페이지
import React, { Component } from 'react';
export default class extends Component {
render() {
return (
<div>제작자: ZeroCho</div>
);
}
}
components/Index.js 메인페이지
import React, { Component } from 'react';
export default class extends Component {
render() {
return (
<div>
<form id="start-screen">
<input id="name-input" placeholder="영웅 이름을 입력하세요!" />
<button id="start">시작</button>
</form>
<div id="screen">
<div id="hero-stat">
<span id="hero-name"></span>
<span id="hero-level"></span>
<span id="hero-hp"></span>
<span id="hero-xp"></span>
<span id="hero-att"></span>
</div>
<form id="game-menu" style="display: none;">
<div id="menu-1">1.모험</div>
<div id="menu-2">2.휴식</div>
<div id="menu-3">3.종료</div>
<input id="menu-input" />
<button id="menu-button">입력</button>
</form>
<form id="battle-menu" style="display: none;">
<div id="battle-1">1.공격</div>
<div id="battle-2">2.회복</div>
<div id="battle-3">3.도망</div>
<input id="battle-input" />
<button id="battle-button">입력</button>
</form>
<div id="message"></div>
<div id="monster-stat">
<span id="monster-name"></span>
<span id="monster-hp"></span>
<span id="monster-att"></span>
</div>
</div>
</div>
);
}
}
components/NoMatch.js 지정하지 않은 주소를 쳤을 때 나오는 컴포넌트
import React from 'react';
export default () => (
<div className="not-found">
<h1>404 NOT FOUND!</h1>
<p>찾으시는 페이지가 없습니다! 주소가 맞나 다시 한 번 확인해주세요!</p>
</div>
);
render.js
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, IndexRoute, Link, browserHistory } from 'react-router';
import App from './components/App.js';
import Index from './components/Index.js';
import About from './components/About.js';
import NoMatch from './components/NoMatch.js';
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Index} />
<Route path="about" component={About}/>
<Route path="*" component={NoMatch}/>
</Route>
</Router>
), document.getElementById('root'))
이렇게 컴포넌트 여러개와 Router가 있는 render.js를 만들어줍니다. 지난 시간까지는 render에 Basic 컴포넌트를 넣었죠? 이번에는 Router를 렌더링하는데요. history 속성으로 browserHistory를 넣어주었습니다. 이것이 있어 일반 웹을 사용하는 것처럼 주소를 바꾸고, 뒤로 가기와 앞으로 가기를 할 수 있습니다.
Router 컴포넌트 안에 Route가 있습니다. 여기서부터 라우팅 지정의 시작입니다. 일단 App 컴포넌트를 여기에 넣고요. Route 안에 다시 세 개의 라우트를 넣어줍니다. IndexRoute와 Route[path='about'], Route[path='*']입니다.
IndexRoute는 /에 해당하는 path에 기본으로 보이는 컴포넌트를 설정하는 곳입니다. Index 컴포넌트를 넣어주었죠. 이렇게 넣은 Index 컴포넌트는 this.props.children
속성으로 App 컴포넌트에 전달됩니다.
나머지는 path가 정해져있습니다. 각각 /about과 /*이네요. *는 모든 라우트를 표시하는 와일드카드 기호입니다. 즉 앞의 /와 /about을 제외한 나머지 라우트를 가리키죠. 여기에 각각 About과 NoMatch 컴포넌트를 넣어주었는데요. 마찬가지로 App 컴포넌트에 this.props.children
속성으로 전달됩니다. 페이지가 늘어날 때마다 컴포넌트를 만든 후 Route에 추가해주면 됩니다.
다음 시간에는 Redux에 대해 알아보도록 하겠습니다!