react-router 및 history

Y·2021년 8월 22일
0
post-thumbnail
post-custom-banner

<BrowserRouter><Router>

BrowserRouter

  • <BrowserRouter>는 클라이언트 사이드 라우팅을 위해 라우팅 관련 컴포넌트들의 최상단에 위치시켜야하는 컴포넌트로, react-router-dom 패키지에 속한다. 사실 이는 react-router 패키지에 속해있는 <Router> 컴포넌트를 래핑한 컴포넌트이다.
  • <BrowserRouter> 컴포넌트는 <Router> 컴포넌트를 렌더링할때 props 로 history 객체를 전달하는데, 이 객체는 history 패키지의 createBrowserHistory() 함수를 호출함으로써 생성된다.

Router

  • <Router> 컴포넌트는 마운트되는 순간에 props로 전달받은 history 객체의 프로퍼티인 location 객체를 자신의 지역 상태에 저장한다.
  • props로 전달받은 history 객체를 구독하여(history.listen 메소드) 브라우저의 현재 URL이 변경될때마다 자신의 지역상태에 해당하는 location 객체가 새로운 location 객체로 대체되도록 한다.
  • 즉, 브라우저의 현재 URL 정보를 <Router> 컴포넌트가 지역상태로서 실시간으로 추적하겠다는 의미이다.

<Switch>

  • <Switch> 는 브라우저의 현재 URL과 매칭되는 첫번째 <Route> 자식 엘리먼트를 렌더링하기 위한 컴포넌트로 react-router 패키지에 속해있다.
  • 이는 <RouterContext.Consumer> 컴포넌트를 렌더링함으로써 RouterContext를 참조한다. 이때 RouterContext의 location 객체 정보와 children props로 전달받은 각 자식 엘리먼트의 path props 정보를 하나씩 비교한다. 그 과정에서 첫번째로 매칭되는 <Route> 자식 엘리먼트를 렌더링하게된다.

<Route>

  • <Route>props로 전달받는 path의 값이 브라우저의 현재 URL과 매칭될때 특정 컴포넌트를 렌더링하는 컴포넌트로 react-router 패키지에 속해있다.
  • 이는 <RouterContext.Consumer> 컴포넌트를 렌더링함으로써 RouterContext를 참조한다.
  • RouterContextlocation 객체 정보와 props로 전달받은 path 값을 비교하여 매칭이 되면 component props로 전달받은 컴포넌트를 렌더링해주고, 아니라면 null을 렌더링한다.
  • 매칭되는 경우, props로는 RouterContext와 동일한 구성의 값들 (match 객체, location 객체, history 객체)을 넘겨준다.
  • <Link>는 페이지의 깜빡임(리로드) 없이 내비게이션을 수행하기 위한 컴포넌트로, react-router-dom 패키지에 속해있다.
  • 결론적으로는 <a> 태그로 렌더링되지만, 일반적인<a> 태그와는 조금 다르게 동작한다. preventDefault() 함수를 호출하여 <a> 태그의 기본 동작을 방지한다. RouterContext에 존재하는 history객체를 이용하여 내비게이션을 수행하도록 구현한다.

    <a> 태그와의 차이점?
    <a> 태그의 기본적인 속성은 페이지를 이동시키면서, 페이지를 아예 새로 불러오게된다. 우리 리액트 앱이 지니고있는 상태들도 초기화되고, 렌더링된 컴포넌트도 모두 사라지며 새로 렌더링을 하게된다.
    그렇기에 <Link> 컴포넌트를 사용하는데, 이 컴포넌트는 HTML5 History API 를 사용하여 브라우저의 주소만 바꿀뿐, 페이지를 새로 불러오지는 않는다.


createBrowserHistory

history object (객체)
BrowserRouterhistory 객체를 자동으로 생성하고, 컴포넌트 단위로 history 객체를 컨트롤하기 때문에 커스텀 작업이 어렵다. 컴포넌트 바깥에서 history 객체를 컨트롤하기 위해서는 (ex. action creator) BrowserRouter 대신 Router를 사용하고, BrowserHistory 를 별도 생성하여 관리해야한다.

history 객체는 history모듈에서 createBrowserHistory로 받아올 수 있다.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'connected-react-router';
import { configureStore } from './store';

const history = createBrowserHistory(); //히스토리 객체 반환
const store = configureStore(history);

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <App />
        </ConnectedRouter>
    </Provider>
, document.getElementById('root'));

이렇게 작성해주는 방법이 있다고 하는데, 우리는 따로 utils 폴더에 history.js 파일에 분리하여 작성해보도록 하자.

// utils > history.js 파일
import { createBrowserHistory } from "history";

export default createBrowserHistory();

이때
기존 App.js 파일에 이렇게 작성되어있을텐데

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Main from '@/pages/Main';
import connectStore from '@/hoc/connectStore';

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/" exact component={Main} />
      </Switch>
    </Router>
  );
};

export default connectStore(App);

상단에 import history from "./utils/history"; 작성한 history 파일을 import 해오고 <Router history={history}> Routerhistoryprops으로 전달한다. 그러면 각 컴포넌트는 history, location, match 객체를 props로부터 제공받아 사용할 수 있게된다.

⛔ 여기에서 상단 import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 이 부분을 보면 BrowserRouter as Router 이렇게 사용했었는데 이렇게 하니까 history가 제대로 작동하지 않았다. BrowserRouter를 제거해줬더니 그제서야 잘 작동한다.

최종 코드는 아래와 같다.

import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import Main from '@/pages/Main';
import connectStore from '@/hoc/connectStore';

const App = () => {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={Main} />
      </Switch>
    </Router>
  );
};

export default connectStore(App);

❗ 왜 그럴까 생각해보면, 위에서 다뤘던 BrowserRouter에서

<BrowserRouter> 컴포넌트는 <Router> 컴포넌트를 렌더링할때 propshistory 객체를 전달하는데, 이 객체는 history 패키지의 createBrowserHistory() 함수를 호출함으로써 생성된다.

라고 했었는데, 이미 우리는 createBrowserHistoryhistory 객체를 만들어서(history.js 파일에서) 넘겨주는 입장이니까 그냥 Router를 사용하는게 맞는 방법인 것 같다.


[References]

profile
기록중
post-custom-banner

0개의 댓글