[React] React Router 사용해보기 (BrowserRouter, HashRouter, withRouter, exact path, Switch, history, location, match, Link)

리액트에서 페이지 이동 즉, 라우터를 사용하기 위해서는 react-router-dom 모듈 패키지를 설치를 해야한다.


리액트 라우터 설치

$ npm install react-router-dom


react-router 도 설치해야 하지만, react-router-dom 패키지 내부에 react-router가 함께 설치되기 때문에  react-router-dom만 설치하면 된다.

여기서 한가지 알아야 할 점은 리액트에서 라우터는 실제로 페이지가 바뀌는것이 아닌 가상으로 바뀌는것으로 웹브라우저가 해당 페이지가 변경되었는지 인식하지 못 할 수 있다.


페이지 이동을 위해서 사용되는 라우팅 방법에는 몇가지가 있다. 

그 중에서 많이 사용되는  BrowserRouter, HashRouter, Route, Link 에 대해서 공부해 봤다.


라우팅을 사용하기 위해서는 

import { BrowserRouter, HashRouter, Route, Link } from 'react-router-dom';

해당 코드를 import 해야한다.


import 한것들에 대해서 간단히 말하면

BrowserRouter와 HashRouter는 라우터 기능을 사용하게 해주는 큰 틀과 같은 역할을 하며

Route는 경로를 지정하고 해당 경로에 대한 출력 화면 즉, 컴포넌트를 설정할 수 있다.

마지막으로 Link는 a 태그와 같이 설정한 경로에대한 컴포넌트를 불러오도록 도와준다.


import React from 'react'
import { BrowserRouter, HashRouter, Route, Link } from 'react-router-dom';
import LoginPage from './components/LoginPage';
import MainPage from './components/MainPage';
import RegisterPage from './components/RegisterPage';


function App() {
return (
<BrowserRouter>
<div>
<Link to='/LoginPage'>로그인 페이지</Link>
&nbsp;
<Link to='/MainPage'>메인 페이지</Link>
&nbsp;
<Link to='/RegisterPage'>등록 페이지</Link>
</div>
<div>
<Route exact path='/LoginPage' component={LoginPage} />
<Route exact path='/MainPage' component={MainPage} />
<Route exact path='/RegisterPage' component={RegisterPage} />

</div>
</BrowserRouter>
)
}

export default App;


최상위 태그로 BrowserRouter 또는 HashRouter 를 사용하여 라우팅에 필요한 모든 태그들을 묶어야 한다.

둘 중에 BrowserRouter를 사용했는데 그 이유는 아래와 같다.



라우터1. BrowserRouter

- Link 컴포넌트 to속성에 이동할 경로를 작성한다.

- Route 컴포넌트 path속성을 Link의 to속성과 매핑하여 Link를 클릭시 해당 경로의 component로 이동시켜준다.

- 새로고침 하면 경로 못찾아서 에러가 발생한다.

- 검색엔진이 왔을때 해당 페이지에 대한 정보를 서버가 알려준다 (보여줄 페이지들이 서버쪽에 셋팅이 된 경우만)



라우터2. HashRouter

- Link 컴포넌트 to속성에 이동할 경로를 작성한다.

- 주소에 해쉬(#)이 붙는다.

- 새로 고침해도 그대로 나온다 -> #뒤에는 화면에서 읽는 경로이기 때문이다

- 검색엔진으로 못읽는 단점때문에 거의 사용하지 않는다.



+추가적으로 
라우터3. withRouter

- Router 컴포넌트 하위에 있지 않지만 Route 기능을 쓰고자 할때
- HOC (Higher-Order Component) 방식으로 컴포넌트를 감싸준다.

import React from 'react';
import { withRouter } from 'react-router-dom'

const TestPage = (props) => {
console.log(props);
return (
<div>
페이지 매치
</div>
)
}

export default withRouter(TestPage);



# Route 컴포넌트가 props로 하위 컴포넌트에게 전달하는 객체

Route의 속성으로 component에 적힌 컴포넌트들은 props로 특정한 값의 객체를 받게 되는데 

이 객체에는 아래와 같은 속성들이 있고 이를 활용하여 많은 것들을 할 수 있다.


history
- 브라우저의 history와 유사한 객체
- go(), goback()등 메서드로 컴포넌트 히스토리 조작 가능
- history.go(숫자) : 양수면 앞으로 음수면 뒤로 이동
- history.push(경로) : 매개변수에 적힌 경로로 이동

location
- 현재 URL 위치와 관련된 정보를 가지고 있음
- 현재 경로가 있는 pathname, 현재 경로의 쿼리스트링을 담고 있는 search등이 있다.

match
- Route 컴포넌트에 있는 path 속성과 현재 url을 가지고 있어서 비교가능하다


만약 history, location, match 가 담긴 객체를 props로 전달받지 못하는 component의 경우 바로 위에서 언급한 withRouter로 감싸주도록 한다.


아래에서 보게될 예제는 위에서 작성한 코드와 달리 동적 params를 사용하여 모든 값들을 처리하였다.

동적 params는 Route에 적혀있는 :Page 부분이다.

Link 컴포넌트의 경로와 동일한 /news/ 부분을 제외한 뒷부분을 동적으로 받아올 수 있다.

그리고 그렇게 받아온 주소를 밑 데이터를 props로 전달하여 하위 컴포넌트에서 해당 객체에 담긴 정보를 통해 여러가지 바로 위에 적혀있는 history, location, match 정보들로 화면을 출력할 수 있다.

App.js
import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import Matching from './components/Matching';

function App() {
return (
<div className="App">
<BrowserRouter>
<div>
<Link to='/news/Page1?query=10&name=person1'>page1</Link><br />
<Link to='/news/Page2'>page2</Link><br />
<Link to='/news/Page3'>page3</Link><br />
<Link to='/news/Matching'>Matching</Link><br />

</div>
<div>
<Route exact path='/news/:Page' component={Matching} />
</div>
</BrowserRouter>
</div>
);
}

export default App;

Matching.js
import React from 'react'
import Page1 from './Page1';
import Page2 from './Page2';
import Page3 from './Page3';

const Matching = (props) => {
console.log(props);
console.log(props.match.params.Page); // page1 page2 page3 Matching
switch (props.match.params.Page) {
case 'Page1':
return <Page1 />
break;
case 'Page2':
return <Page2 />
break;
case 'Page3':
return <Page3 />
break;
default:
return <div>일치하는 페이지가 없습니다.</div>
}
}

export default Matching





props로 전달받은 객체에 담긴 정보들을 가지고 조건식에 따라 컴포넌트를 출력할 수 있으며

?query=10&name=person 과 같은 쿼리스트링도 ?물음표를 기준으로 받아와 각각의 값에 따라 데이터를 처리할 수도 있다.

쿼리스트링은 객체안에 location 안에 search로 값을 받아온다.

받아온 쿼리스트링은 URLSearchParams를 통해 구문을 분석하여 데이터를 출력할 수 있다.


Route의 갯수를 줄이기 위해서 동적 라우팅을 사용했지만 

결국 동적 라우팅에 대해서 특정 컴포넌트에서 조건식으로 일일이 분기 처리 해주는 조건식을 사용해야 하기때문에 동적 params로 Route의 갯수를 줄인다고 코드의 양이 획기적으로 줄어들지는 않는다.

그러므로

조건식을 길게 작성하여 분기처리를 하느냐 아니면 그냥 Route를 쭉 작성하여 사용하느냐는 본인이 편한것을 선택하면 될 것 같다.


# exact path

Route 안에  path 속성을 작성할 때 path 앞에 exact 키워드를 사용했다.

<Route exact path='/Home' component={LoginPage} />
<Route exact path='/Home/MainPage' component={MainPage} />
<Route exact path='/Home/RegisterPage' component={RegisterPage} />

exact 키워드 없이 path 속성으로만 작성하게 될 때 문제점을 알아보면 

예를들어 https://app.com/Home 해당 경로로 이동한다면 Home 컴포넌트로 정상적으로 이동한다.

하지만 https://app.com/Home/MainPage 경로로 이동하려고 할 경우에도 Home 컴포넌트로 이동하게 된다.

원래는  MainPage로 이동해야 하는데 말이다.

그 이유는 Router가 부분적으로만 닮아도 동일한 경로라고 인식해버려서  처음 보는 Route의 컴포넌트로 이동시켜버리기 때문이다.

그래서 부분적인것만 닮아도 같은것으로 인식하는 문제를 해결하기 위해서 exact 키워드를 함께 작성해야 한다.

exact path를 사용하면 작성한 경로와 완벽하게 일치해야지만 해당 컴포넌트를 출력한다.


# Switch

동일한 경로를 가리키고있는 Route가 존재하면 두개의 컴포넌트 모두를 화면에 출력할 수 있다.

이러한 결과를 방지하기 위해서 Switch를 사용한다. 

Switch를 사용하게 되면 가장 먼저 일치하는 경로를 실행한다.

switch 조건문처럼 위에서부터 순차적으로 비교하면서 실행된다.

ex1)
<BrowserRouter>
<div>
<Link to='/news/Page1?query=10&name=person1'>page1</Link><br />
<Link to='/news/Page2'>page2</Link><br />
<Link to='/news/Page3'>page3</Link><br />
<Link to='/news/Matching'>Matching</Link><br />

</div>
<div>
<Route path='/' component={Matching} />
<Route path='/news/:Page' component={Matching} />
</div>
</BrowserRouter>




/ 경로는 모든 컴포넌트가 가지고 있는 경로라서 두가지 모두 출력된다.


ex2)
<BrowserRouter>
<div>
<Link to='/news/Page1?query=10&name=person1'>page1</Link><br />
<Link to='/news/Page2'>page2</Link><br />
<Link to='/news/Page3'>page3</Link><br />
<Link to='/news/Matching'>Matching</Link><br />

</div>
<div>
<Switch>
<Route exact path='/' component={Matching} />
<Route exact path='/news/:Page' component={Matching} />
</Switch>
</div>
</BrowserRouter>


이처럼 Switch와 exact 를 사용해서 정확한 경로를 찾아 원하는 컴포넌트만 출력하게 할 수 있다. 

그러므로 Switch와 exact 를 필수적으로 사용하도록 하는것이 좋다.


# Link

Route 에 지정한 경로로 이동하도록 하는 링크를 생성해 준다.

import React from 'react'
import { BrowserRouter, HashRouter, Route, Link } from 'react-router-dom';
import LoginPage from './components/LoginPage';
import MainPage from './components/MainPage';
import RegisterPage from './components/RegisterPage';


function App() {
return (
<BrowserRouter>
<div>
<Link to='/Home/LoginPage'>로그인 페이지</Link>
&nbsp;
<Link to='/Home/MainPage'>메인 페이지</Link>
&nbsp;
<Link to='/Home/RegisterPage'>등록 페이지</Link>
</div>
<div>
<Route exact path='/Home/LoginPage' component={LoginPage} />
<Route exact path='/Home/MainPage' component={MainPage} />
<Route exact path='/Home/:page' component={RegisterPage} />

</div>
</BrowserRouter>
)
}

export default App;

to 속성을 이용하여 경로를 지정해준다.

html의  a태그와 같은 역활을 하며 DOM에도 a태그로 표시된다. 

하지만 a태그와 다른점은  새로고침없이 페이지를 이동할 수 있다는 점과

Link로 작성할 경우 리액트만 알고있는 가상의 경로이기 때문에 위에서 언급했던 경로를 직접 입력하거나 새로고침시에 경로를 못찾아서 에러가 발생한다.


하지만 서버측에서 express Router에 해당 주소 경로를 등록하면 에러없이 동작하게 할 수 있다.

마지막으로 Link 는 고정된 레이아웃이며 Route는 페에지에서 변경되는 레이아웃에 속한다.


댓글