라우팅 방식
-
브라우저에 의한 라우팅
- HTML로만 웹페이지를 구성했을 때 사용하던 전통적인 방법
<a>
태그를 활용하거나, JavaScript에서 location.href 속성을 활용해 페이지 이동
-
JavaScript 단에서 라우팅 흉내내기
- 화면 구성 후, 그 화면 콘텐츠에 맞게 주소창 주소를 임의로 변경(부여)하는 방식
- 리액트는 SPA 방식으로 개발하는 것이 보다 적합
react-router-dom
라이브러리 활용
BrowserRouter, Link, Route 써보기
리액트에서 SPA 방식으로 웹페이지를 구현하려면 <a>
태그는 사용하면 안 된다. <a>
태그를 쓰면 페이지 이동 개념이라 페이지 전체가 다시 새로 렌더링되므로 SPA 조건에 위배된다.
<a>
태그를 썼을 때 정말 페이지 전체가 다시 그려지는지 확인해보자.
메인화면, about 페이지, about company 페이지, profile 페이지, blog 페이지로 구성된 웹사이트를 만들었다. 각 페이지는 별도 컴포넌트로 만들었다.
import React from "react";
const App = () => {
return (
<div>
<h1>App10</h1>
<ul>
<li>
<a href="/about/">about</a>
</li>
<li>
<a href="/about/company/">about company</a>
</li>
<li>
<a href="/profile/">profile</a>
</li>
<li>
<a href="/blog/">blog</a>
</li>
</ul>
<AboutPage />
<ProfilePage />
<BlogPage />
</div>
);
};
// /about/
const AboutPage = () => {
return (
<div>
<h2>About Page</h2>
</div>
);
};
// /about/company/
const AboutCompanyPage = () => {
return (
<div>
<h2>About Company Page</h2>
</div>
);
};
// /profile/
const ProfilePage = () => {
return (
<div>
<h2>Profile Page</h2>
</div>
);
};
// /blog/
const BlogPage = () => {
return (
<div>
<h2>Blog Page</h2>
</div>
);
};
export default App;
파비콘을 보면 페이지 전체가 새로 그려지고 있다는 걸 알 수 있다.
BrowserRouter, Link, Route를 사용해 SPA를 구현해보자. 우선 react-router-dom 패키지를 설치한다.
yarn add react-router-dom
Link, Route는 반드시 BrowserRouter 안에서 써야한다. BrowserRouter가 context API를 통해 현재 라우팅 정보를 전달하기 때문이다.
<BrowserRouter></BrowserRouter>
는 보통 앱 최상위에서 감싸준다.
import React from "react";
import { BrowserRouter, Link, Route } from "react-router-dom";
const App = () => {
return (
<BrowserRouter>
<div>
<h1>App10</h1>
<ul>
<li>
<Link to="/about/">about</Link>
</li>
<li>
<Link to="/about/company/">about company</Link>
</li>
<li>
<Link to="/profile/">profile</Link>
</li>
<li>
<Link to="/blog/">blog</Link>
</li>
</ul>
<Route path="/about/" component={ AboutPage } />
<Route path="/about/company/" component={ AboutCompanyPage } />
<Route path="/profile/" component={ ProfilePage } />
<Route path="/blog/" component={ BlogPage } />
</div>
</BrowserRouter>
);
};
// /about/
const AboutPage = () => {
return (
<div>
<h2>About Page</h2>
</div>
);
};
// /about/company/
const AboutCompanyPage = () => {
return (
<div>
<h2>About Company Page</h2>
</div>
);
};
// /profile/
const ProfilePage = () => {
return (
<div>
<h2>Profile Page</h2>
</div>
);
};
// /blog/
const BlogPage = () => {
return (
<div>
<h2>Blog Page</h2>
</div>
);
};
export default App;
그런데 /about/company/
페이지에서 about 컴포넌트와 about company 컴포넌트가 모두 리턴된다. Route는 매칭되는 모든 컴포넌트가 이어서 렌더링 되기 때문인데, 주소가 /about/
이라는 조건에 부합하므로 두 컴포넌트가 모두 리턴되는 것이다.
필요한 컴포넌트만 렌더링하기 위해 정확히 일치할 때만 리턴하는 exact
속성을 추가한다. (기본값 exact={true}
)
<Route exact path="/about/" component={ AboutPage } />
<Route exact path="/about/company/" component={ AboutCompanyPage } />
<Route exact path="/profile/" component={ ProfilePage } />
<Route path="/blog/" component={ BlogPage } />
/blog/post/1
, blog/post/2
이렇게 주소가 계속 뒤에 붙는 경우는 exact를 붙이지 않는 것이 맞다.
NavLink 써보기
Link
와 유사하나, activeStyle
과 activeClassName
속성을 지원한다. 스타일 속성과 클래스 이름을 객체 형태 속성값으로 넘겨받아 동적으로 적용할 수 있다.
속성을 이렇게 추가해주고,
const navActiveStyle = {
fontWeight: 'bold',
backgroundColor: 'yellow'
};
컴포넌트에 activeStyle 속성을 추가해준다.
import React from "react";
import { BrowserRouter, NavLink, Route } from "react-router-dom";
const App = () => {
return (
<BrowserRouter>
<div>
<h1>App10</h1>
<ul>
<li>
<NavLink exact to="/about/" activeStyle={ navActiveStyle }>about</NavLink>
</li>
<li>
<NavLink exact to="/about/company/" activeStyle={ navActiveStyle }>about company</NavLink>
</li>
<li>
<NavLink to="/profile/" activeStyle={ navActiveStyle }>profile</NavLink>
</li>
<li>
<NavLink to="/blog/" activeStyle={ navActiveStyle }>blog</NavLink>
</li>
</ul>
<Route exact path="/about/" component={ AboutPage } />
<Route exact path="/about/company/" component={ AboutCompanyPage } />
<Route exact path="/profile/" component={ ProfilePage } />
<Route path="/blog/" component={ BlogPage } />
</div>
</BrowserRouter>
);
};
const navActiveStyle = {
fontWeight: 'bold',
backgroundColor: 'yellow'
};
// /about/
const AboutPage = () => {
return (
<div>
<h2>About Page</h2>
</div>
);
};
// /about/company/
const AboutCompanyPage = () => {
return (
<div>
<h2>About Company Page</h2>
</div>
);
};
// /profile/
const ProfilePage = () => {
return (
<div>
<h2>Profile Page</h2>
</div>
);
};
// /blog/
const BlogPage = () => {
return (
<div>
<h2>Blog Page</h2>
</div>
);
};
export default App;
about company 컴포넌트를 선택했을 때 about 컴포넌트까지 스타일이 적용되는데, Route
와 마찬가지로 NavLink
에도 exact
속성을 추가해서 해결할 수 있다.
<NavLink exact to="/about/" activeStyle={ navActiveStyle }>about</NavLink>
<NavLink exact to="/about/company/" activeStyle={ navActiveStyle }>about company</NavLink>
<NavLink to="/profile/" activeStyle={ navActiveStyle }>profile</NavLink>
<NavLink to="/blog/" activeStyle={ navActiveStyle }>blog</NavLink>
Switch와 No Match 처리
Switch
를 이용해 잘못된 경로로 접근했을 때(=일치하는 컴포넌트가 없을 때) 내보낼 컴포넌트를 생성할 수도 있다.
import { BrowserRouter, Route, NavLink, Switch } from "react-router-dom";
const RouteNoMatch = () => {
return <div>잘못된 경로로 접근하셨습니다.</div>;
};
그리고 컴포넌트는 <Switch></Switch>
로 묶어준다.
<Switch>
<Route exact path="/about/" component={ AboutPage } />
<Route exact path="/about/company/" component={ AboutCompanyPage } />
<Route exact path="/profile/" component={ ProfilePage } />
<Route path="/blog/" component={ BlogPage } />
<Route component={ RouteNoMatch } />
</Switch>
어떤 경로로 접속했을 때 가장 위에 있는 컴포넌트부터 일치 여부를 확인 후, 아무것도 일치하는 게 없으면 RouteNoMatch
컴포넌트를 렌더링하도록 할 수 있다.
Route로 설정한 컴포넌트가 받는 3가지 props
-
history: 히스토리 조작
.location
,.push(...)
,.replace(...)
,.goBack()
,.goForward()
-
location: 현재 경로 정보
.hash
,.pathname
,.search
,.state
속성
-
match: Router 매칭 정보
.isExact
,.url
,.path
,.params
속성
location props 사용하기
location
을 사용해 잘못 접속한 경로를 보여줄 수도 있다.
콘솔에 location을 찍어보면 객체가 어떻게 구성되어있는지 볼 수 있다.
pathname 키로 접근하면 경로를 얻을 수 있겠다.
const RouteNoMatch = ({ location }) => {
return <div>잘못된 경로로 접근하셨습니다. 접속 경로: { location.pathname }</div>;
};
match props의 .url, .params 속성 이용하기
url 하위 경로에 따라 렌더링할 내용을 다르게 지정할 수 있다.
블로그 포스트 내용을 담을 PostDetail 컴포넌트를 추가한다.
const PostDetail = ({ match }) => {
const { params: { post_id }, } = match;
return (
<div>
<h2>Post Detail #{ post_id }</h2>
</div>
)
}
기존 BlogPage 컴포넌트에는 이동할 링크를 생성한다. <a>
태그를 사용하면 SPA가 아니게 되므로 Link
나 NavLink
를 사용한다.
const BlogPage = ({ match }) => {
return (
<div>
<h2>Blog Page</h2>
<ul>
<li>
<Link to={`${match.url}100/`}>100번 글</Link>
</li>
<li>
<Link to={`${match.url}101/`}>101번 글</Link>
</li>
</ul>
</div>
);
};
post id값을 받아 PostDetail을 렌더링 하려면 blog Route보다 위에 위치시켜야 한다.
<Switch>
<Route exact path="/about/" component={ AboutPage } />
<Route exact path="/about/company/" component={ AboutCompanyPage } />
<Route exact path="/profile/" component={ ProfilePage } />
<Route path="/blog/:post_id/" component={ PostDetail } />
<Route path="/blog/" component={ BlogPage } />
<Route component={ RouteNoMatch } />
</Switch>
QueryString 처리
query-string 라이브러리를 사용해 QueryString으로 넘어온 값을 파싱해 사용할 수도 있다. 다만 이 라이브러리는 넘어온 값을 객체로 파싱해주므로 값을 숫자 타입으로 사용하려면 적절히 변환해주어야 한다.
yarn add query-string
location 내 search 키 값이 쿼리스트링 값을 담고 있으므로 그것을 파싱하면 된다.
const ProfilePage = ({ location }) => {
console.log('location: ', queryString.parse(location.search));
return (
<div>
<h2>Profile Page</h2>
</div>
);
};
콘솔에 찍어보니 잘 나온다.
쿼리스트링 값을 변수에 할당해 어딘가에 이용해보자.
const ProfilePage = ({ location }) => {
const { token } = queryString.parse(location.search);
return (
<div>
<h2>Profile Page</h2>
token: { token }
</div>
);
};