~~React Router v4가 정식으로 나와서 3은 deprecated 된 거나 마찬가지라고 본다.~~~ 현재 React Router v3은 디프리케잇 되지 않고 React Router v4와 다른 노선을 탔을 뿐, 지원은 계속 해주는 것 같다. 따라서 레거시 환경을 싫어하기도 해서 한 번 마이그레이션을 간단하게 해보았다. 기본적으로 웹팩, 바벨, 리액트 라우터 등등은 안다는 전제 하에 글을 썼다. 웹팩 2에 대한 모르는 부분은 아래 글을 조금씩 참조하면 도움이 될 것이다.
소스 코드는 정재남 님께서 제공해주신 소스를 이용했다. v3으로 만든 라이브 데모에서 직접 내용을 확인해보고 시작하자. 혹시 만들기 귀찮거나 중간중간 확인할 사람은 소스 코드를 보도록 하자. 서버는 없으니 새로고침을 하면 제대로 정보를 표시 못 하지만, 우리가 실제 진행할 때는 Node.js의 express 프레임워크를 사용해서 새로고침을 해도 제대로 작동하게 만들 것이다.
전체적인 프로젝트 구조는 다음과 같다.
일단 packge.json 파일을 만들고 다음 패키지들을 설치하자.
1 2
npm i -D babel-core babel-loader babel-plugin-syntax-dynamic-import babel-preset-env babel-preset-react webpack-dev-server npm i -S express html-webpack-plugin react react-dom react-router@^3.x webpack
babel-plugin-syntax-dynamic-import는 코드 스플리팅을 위해 추가한 플러그인이다. .babelrc 파일을 만들고 아래와 같이 적어주자.
component 대신에 getComponent를 써서 코드 스플리팅을 하였다. 또한 그 안에 import(component)와 같은 구문 때문에 babel-plugin-syntax-dynamic-import를 쓰게 된 거다. 혹시 코드 스플리팅에 대해 잘 모르는 사람은 (Webpack 2) 코드를 분할해보자!를 봐보자.
1 2 3 4 5 6 7 8 9 10 11 12 13
// index.js if( // 배포할 때는 리액트 개발 도구를 죽여놔야함. process.env.NODE_ENV === 'production' && window.__REACT_DEVTOOLS_GLOBAL_HOOK__ && Object.keys(window.__REACT_DEVTOOLS_GLOBAL_HOOK__._renderers).length ) window.__REACT_DEVTOOLS_GLOBAL_HOOK__._renderers = {};
module.exports = { devtool: 'inline-source-map', entry: [ `webpack-dev-server/client?http://localhost:${PORT}`, // bundle the client for webpack-dev-server // and connect to the provided endpoint
'webpack/hot/only-dev-server', // bundle the client for hot reloading // only- means to only hot reload for successful updates
resolve(ROOT, 'index') // the entry point of our app ], output: { publicPath: '/', filename: 'bundle.js', }, plugins: [ new webpack.HotModuleReplacementPlugin(), // enable HMR globally
new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates
// import { BrowserRouter, HashRouter, Route, Redirect, Switch } from 'react-router-dom'; // 리액트 라우터를 코드 스플리팅 하려면 위 코드를 이렇게 하나씩 불러와야한다 귀찮 ㅠ importBrowserRouterfrom'react-router-dom/es/BrowserRouter'; importHashRouterfrom'react-router-dom/es/HashRouter'; importRoutefrom'react-router-dom/es/Route'; importRedirectfrom'react-router-dom/es/Redirect'; importSwitchfrom'react-router-dom/es/Switch';
importMenufrom'./components/Menu';
// HTML5 History API 지원여부 파악 const isBrowserHistory = history.pushState; constRouter = isBrowserHistory ? BrowserRouter : HashRouter;
// 리액트 라우터 4에서 코드 스플리팅 하기. // getComponent is a function that returns a promise for a component // It will not be called until the first mount constasyncComponent = getComponent => ( classAsyncComponentextendsComponent { constructor() { super(); this.state = {Component: AsyncComponent.Component}; }
constApp = () => ( // v3에는 Router 속성에 browserRouter or hashRouter가 들어갔는데, // v4에는 BrowserRouter or HashRouter가 Router까지 포함한다. // 또한 Router 안에는 하나의 컴포넌트만 들어가야한다. // 따라서 div 같은 컴포넌트로 그 안을 한 번 감싸줘야한다. // 또한 IndexRoute는 Route 컴포넌트의 exact라는 속성으로 대체되었다. // Redirect 컴포넌트는 Switch 컴포넌트로 감싸줘야 정상 작동한다. // 또한 파라미터는 괄호를 써서 생략 가능하던 것이 불가능해졌다. <Router> <div> <Menu/> <Routeexactpath="/"component={Home} /> <Routepath="/about"component={About} /> <Routepath="/about/name"component={Name} /> <Switch> <Redirectto="/portfolio/0"from="/about/redirect0"/> <Redirectfrom="/about/redirect1"to="/portfolio/1"/> </Switch> <Routeexactpath="/portfolio"component={Portfolio} /> <Routepath="/portfolio/:id"component={Portfolio} /> </div> </Router> );
exportdefaultApp;
이렇게 하면 일단 내가 먼저 react-router v3으로 작성했던 내용들은 마이그레이션 됐다. npm start와 npm build를 통해 확인해보자. 더 자세한 사용 방법들은 무조건 공식 사이트를 참조하자.
그리고 여기서 끝난 게 아니다. 리액트 라우터 4는 리액트 핫 로더와도 문제가 있어서 리액트 핫 로더 3에서 해결했다고는 하지만… 이 또한 어느 정도 문제가 존재해 추후에 해당 내용을 다뤄봐야겠다.