1-4. 라우트 속의 라우트
react-router 가 버전 4로 업데이트 되면서 달라진 점 중 하나는, Route 내부에 Route 를 설정하는 방식이 달라졌다는 것 입니다.
이전에는, 다음과 같은 형식으로 라우트를 선언하고:
<Route path="foo" component={Foo}>
<Route path=":id" component={Bar}/>
</Route>
Foo 컴포넌트에서 props.children 의 자리에 Bar 컴포넌트가 들어가는 형식이였습니다. 그래서 모든 라우트는 최상위에서 정해주어야 했죠.
하지만 이제는 다릅니다. v4 에서는, props.children 을 사용하지 않고 라우트에서 보여주는 컴포넌트 내부에 또 Route 를 사용 할 수 있게 됐습니다. 한번 해볼까요?
먼저 Post 라는 페이지 컴포넌트를 만드세요. 이 컴포넌트에서는 params.id 를 받아와서 렌더링해줍니다.
src/pages/Post.js
import React from 'react';
const Post = ({match}) => {
return (
<div>
포스트 {match.params.id}
</div>
);
};
export default Post;
그 다음엔 Posts 페이지 컴포넌트를 만드세요. 이 컴포넌트에 Link 에서 현재 주소 뒤에 id 를 붙여서 이동하도록 설정하세요. 그리고 Link 리스트 하단에는 Route 를 통해 조건에 따라 원하는 결과를 보여주도록 설정하겠습니다.
src/pages/Posts
import React from 'react';
import { Link, Route } from 'react-router-dom';
import { Post } from 'pages';
const Posts = ({match}) => {
return (
<div>
<h2>Post List</h2>
<ul>
<li><Link to={`${match.url}/1`}>Post #1</Link></li>
<li><Link to={`${match.url}/2`}>Post #2</Link></li>
<li><Link to={`${match.url}/3`}>Post #3</Link></li>
<li><Link to={`${match.url}/4`}>Post #4</Link></li>
</ul>
<Route exact path={match.url} render={()=>(<h3>Please select any post</h3>)}/>
<Route path={`${match.url}/:id`} component={Post}/>
</div>
);
};
export default Posts;
상단부터 코드 설명을 하겠습니다. Post 컴포넌트를 불러올때 './Post' 에서 바로 불러오는게 아닌 'pages' 를 통해 불러왔습니다. 지금 상황에서는 상대경로에서 불러오는게 더 자연스러울수도 있긴 합니다. 하지만, 우리는 2장에서 코드스플리팅을 해보게 될 텐데, 이 과정에서 페이지를 불러오는 방식이 통일되어야 제대로 작동하므로 pages 에서 불러오도록 설정하였습니다.
Link 를 설정하는 부분에서는, match.url 이 사용되었는데요, 이 url 은 현재의 라우트의 경로를 알려줍니다.
이 부분에서는 그냥 to="/posts/1" 으로 설정을 해도 됩니다. 하지만, 이렇게 하는 경우에 나중에 현재의 라우트 경로가 바뀌게 되면 자동으로 바뀐다는 장점이 있습니다.
하단에 Route 를 설정 할 때 첫번째 라우트는 match.url 로 설정하였습니다. 이 의미는, 포스트의 id 가 주어지지 않았을 때를 의미합니다. 그리고 여기서는 component 대신에 render 가 되었지요? 이 render 는 지금처럼 인라인 렌더링을 가능케 해줍니다.
두번째 라우트에선 현재 라우트의 주소에 :id 가 붙었을 시에 Post 컴포넌트를 보여주도록 설정했습니다.
완성하였다면, 페이지 인덱스를 수정하세요.
src/pages/index.js
export { default as Home } from './Home';
export { default as About } from './About';
export { default as Posts } from './Posts';
export { default as Post } from './Post';
그 다음엔 App 에서 /posts 경로를 위한 라우트를 설정하세요.
src/App.js
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import { Home, About, Posts } from 'pages';
import Menu from 'components/Menu';
class App extends Component {
render() {
return (
<div>
<Menu/>
<Route exact path="/" component={Home}/>
<Switch>
<Route path="/about/:name" component={About}/>
<Route path="/about" component={About}/>
</Switch>
<Route path="/posts" component={Posts}/>
</div>
);
}
}
export default App;
마지막으로, Menu 컴포넌트에서 /posts 로 연결하는 링크를 넣고 잘 작동하는지 확인해보세요.
src/components/Menu.js
import React from 'react';
import { NavLink } from 'react-router-dom';
const Menu = () => {
const activeStyle = {
color: 'green',
fontSize: '2rem'
};
return (
<div>
<ul>
<li><NavLink exact to="/" activeStyle={activeStyle}>Home</NavLink></li>
<li><NavLink exact to="/about" activeStyle={activeStyle}>About</NavLink></li>
<li><NavLink to="/about/foo" activeStyle={activeStyle}>About Foo</NavLink></li>
<li><NavLink to="/posts" activeStyle={activeStyle}>Posts</NavLink></li>
</ul>
<hr/>
</div>
);
};
export default Menu;
헷갈리는 값들
라우트가 받는 props 중에서, 상당히 헷갈리는 값들이 있습니다.
- location.pathname
- match.path
- match.url
비슷한것들 같은데, 대체 어떻게 다를까요?
한번 Post 와 Posts 에서 이 값들을 화면에 렌더링해보겠습니다.
location.pathname 은 현재 브라우저상의 위치를 알려줍니다. 이 값은 어떤 라우트에서 렌더링하던 동일합니다.
match 관련은 설정한 Route 와 직접적으로 관계된 값만 보여줍니다.
- Posts 를 보여주는 라우트에선 :id 값을 설정하지 않았으니 path 와 url 이 둘다 /posts 입니다.
- Post 를 보여주는 라우트에선 path 의 경우엔 라우트에서 설정한 path 값이 그대로 나타납니다. url 의 경우엔 :id 부분에 값이 들어간 상태로 나타납니다.
어때요? 이렇게 보니까 더 이상 헷갈리지 않지요?