Node.js를 이용해서 중고거래 사이트를 만들었는데, 이번에는 리액트를 사용하여 팀프로젝트를 하게 됐다.
그래서 리액트를 사용하면 어떻게 라우팅을 해야하는지 정리하며 익혀보려 한다.
우선 npm으로 yarn을 설치하고, ' yarn create react-app 프로젝트명 ' 으로 프로젝트 폴더를 생성했다.
리액트에서 라우팅을 하기 위해 터미널의 기본 프로필을 cmd로 설정한 후
yarn add react-router-dom
yarn으로 웹용 패키지인 react-router-dom을 설치했다.
React-snippets 확장을 설치하면 rafce 단축키를 써서 리액트 기본코드를 만들 수 있다.
기본 컴포넌트 준비
src/App.js
import {Route, Link, NavLink} from 'react-router-dom';
Route
어떤 규칙을 가진 경로에 어떠한 컴포넌트를 보여줄지 정의할 수 있다.
<Route path="주소규칙" component={보여줄 컴포넌트}/> 의 형식으로 사용 가능하다.
Link
페이지를 새로 불러오지 않고 애플리케이션은 그대로 유지한다. history API를 사용해서 페이지의 주소만 변경한다.
<Link to ="주소">내용</Link> 의 형식으로 사용 가능하다. 쉽게 생각하면 새로고침을 하지 않는 <a> 태그 쯤 되는 것 같다.
NavLink
현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일이나 CSS클래스를 적용할 수 있는 컴포넌트이다.
NavLink에서 링크가 활성화되었을때의 스타일 적용할때는 activeStyle, CSS클래스를 적용할때는 activeClassName 값을 props로 넣어주면 된다.
src/index.js
import { BrowserRouter } from 'react-router-dom';
...
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
BrowserRouter
HTML5의 history API를 사용해서 새로고침하지 않아도 주소를 변경하고, 현재 주소에 관련된 정보를 props로 조회하여 사용할 수 있게 한다.
리액트 라우터를 적용할때는 BrowserRouter로 감싸줘야 한다.
라우트 설정하기
src/App.js
import React from 'react';
import { Route, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
const App = () => {
return (
<div>
//링크
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
//컴포넌트 보여주기
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} /> //경로 다중연결
</div>
);
};
export default App;
라우트를 설정할 때에는 path로 주소를 설정하고, 보여줄 컴포넌트를 적는다.
위의 예시에서 exact를 적지 않으면 /about이나 /info 주소를 입력했을 때 Home 컴포넌트와 About이 같이 보이는데, /about에도 /이 포함되어 있기 때문이다.
때문에 컴포넌트가 함께 불러지는 것을 방지하기 위해 exact를 붙였다. exact를 붙이면 주어진 경로와 정확히 맞아떨어져야만 컴포넌트를 보여준다.
이 때 react-router-dom에서 Switch 를 import 하면 exact 대신 <Switch></Switch>로 라우트들을 감싸서 매칭되는 첫번재 라우트만 보여주고 나머지는 보여주지 않는 식으로 진행할 수도 있다.
요약)
앞부분에 같은 주소를 포함한 라우트들을 설정할 때 지속해서 보여줄 컴포넌트가 아니라면 exact={true}를 붙여주자.
src/Home.js
import React from 'react';
const Home = () => {
return (
<div>
<h1>우리집</h1>
</div>
);
};
export default Home;
Home 컴포넌트(src/Home.js)를 구성해서 export 해주고, 이를 App 컴포넌트(src/App.js)에서 import로 받아 라우트에 맞춰, 즉 기본 페이지에서 Home 컴포넌트를 보여주었다.
상단의 Home 링크는 link to 태그를 통해 지정해준 메인 주소로 이동하게 된다.
라우트 파라미터 받기
라우트 경로에 특정 값을 넣기 위해서는 params를 이용하는 방법과, query를 이용하는 방법이 있다.
params 정보는 profile/:username 형식이고, URL query 정보는 profile?username=maggot 형식이다.
파라미터를 다루기 전 알아둬야 할 것이 있다.
라우트로 설정한 컴포넌트는 history, match, location 3가지의 props를 전달받게 된다.
history 객체는 push, replace, goBack 등을 통해 다른 경로로 이동하거나 앞뒤 페이지로 갈 수 있게 해준다.
match 객체는 어떤 라우트에 매칭됐는지, 즉 어떤 경로규칙에 의해 보이는지에 대한 정보, params 정보를 가지고 있다.
location 객체는 현재 경로에 대한 정보, URL query 정보를 가지고 있다.
그러므로 params 정보인 profile/:username 형식을 사용하려면 match,
URL 쿼리 정보인 profile?username=maggot 형식을 사용하려면 location 객체를 이용하면 된다.
URL query 정보 - location 사용
쿼리는 location 객체의 search 값에서 조회할 수 있다.
//ex)
{
"pathname":"/about",
"search":"detail=true",
"hash":""
}
쿼리를 쓸때 쿼리 문자열을 객체로 파싱하는 과정에서의 결과값은 언제나 문자열이다.
?value=1->("1") , ?value=true->("true")
그러므로 전자처럼 숫자를 받아와야 하는 상황이면 parseInt를 사용해야 한다.
후자처럼 논리값을 사용하는 경우에는 정확히 true라는 문자열과 일치하는지 비교해야 한다.
yarn add qs 를 한 후 About 컴포넌트에서 사용해보자.
src/About.js
import React from 'react';
import qs from 'qs';
const About = ({ location }) => {
const query = qs.parse(location.search, {
ignoreQueryPrefix: true, //문자열 맨 앞에 ? 생략
});
const showDetail = query.detail === 'true'; //쿼리의 파싱 결과는 문자열
return (
<div>
<h1>어바웃 페이지</h1>
<p>여기는 어바웃 페이지 임.</p>
{showDetail && <p>디테일 값은 true</p>}
</div>
);
};
export default About;
ignoreQueryPrefix 로 쿼리문자열 맨 앞 ?을 생략해준다.
그리고 showDetail이면, 즉 query.detail 이 'true'라는 문자열이면 <p>태그가 걸린 '디테일 값은 true'를 출력하게 한다.
params 정보 - match 사용
src/App.js
...
<li>
<Link to="/profiles">프로필</Link>
</li>
...
<Route path="/profiles" component={Profiles} />
...
src/Profiles.js
import React from 'react';
import { Route, Link, NavLink } from 'react-router-dom';
import Profile from './Profile';
const Profiles = () => {
const activeStyle = {
background: 'black',
color: 'white',
};
return (
<div>
<h3>사용자 목록:</h3>
<ul>
<li>
<NavLink activeStyle={activeStyle} to="/profiles/kyungil">
경일
</NavLink>
</li>
<li>
<NavLink activeStyle={activeStyle} to="/profiles/academy">
academy
</NavLink>
</li>
</ul>
<Route
path="/profiles"
exact={true}
render={() => <div>사용자 선택</div>}
/>
<Route path="/profiles/:username" component={Profile} />
</div>
);
};
export default Profiles;
src/Profile.js
import React from 'react';
const data = {
kyungil: {
name: '놀부',
description: '욕심쟁이',
},
academy: {
name: '홍길동',
description: '동에번쩍 서에번쩍',
},
};
const Profile = ({ match }) => {
const { username } = match.params;
const profile = data[username];
if (!profile) {
return <div>사용자 없음</div>;
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
</div>
);
};
export default Profile;
실행 결과를 보면 src/Profile.js에서 match.params로 경로의 /:username 부분에 들어간 kyungil을 불러온 것을 알 수 있다.
만약 사용자 프로필 데이터인 data 에 params로 받아온 username과 일치하는 이름의 data 객체가 없다면 '사용자 없음'을 띄우게 된다.
이 때 헷갈렸던 부분이 있다.
const { username } = match.params;
Profile.js에서 이렇게 {username}에 params 값을 넣고 return 부분에서
<h3>
{username}({profile.name})
</h3>
로 /:username 부분에 입력된 값(kyungil)을 받았는데,
위 코드에서 {username} 대신 쓸 수 있는 코드가 {match.params}가 아닌
<h3>
{match.params.username}({profile.name})
</h3>
이라는 것이다.
match.params에 kyungil이 들어있는 건지, match.params.username에 kyungil이 들어있는 건지 아직 구분이 잘 가지 않는다. 아직 문법적 개념이 부족한 거 같으므로 한동안 태그 안에서 중괄호로 값을 부를때는 콜론 뒤에 인자까지 붙이는 것으로 기억해야 할 것 같다.