여기선 기본적으로 웹팩 1, 바벨, ES2015(ES6)을 알고 있다는 전제로 진행한다. 리액트 대신에 다른 서드 파티(라이브러리/프레임워크) 가지고 테스트하면서 이 글을 봐도 된다. 또한 이 글을 보고 나서 (Webpack 2) 코드를 분할해보자!도 보는 걸 추천한다.
트리 쉐이킹(Tree Shaking)??
트리 쉐이킹이란 나무를 흔들어서 필요없는 걸 떨어트리는 행위를 말한다. 여기서 우리에게 필요없는 것이란 쓰지 않는 코드를 뜻한다.
우리의 코드를 트리 쉐이킹 해보자
기존 웹팩 1에서 번들링이 어떻게 이루어졌는지 보자.
우선 모듈을 하나 만들어보자. (module.js)
1 2
exportconst a = 123123123123; exportconst b = 45645646456;
그리고 이 모듈을 불러다 쓰는 우리의 앱을 만들자. (app.js)
1 2
import {a} from'./module'; console.log(a);
하지만 번들링을 해보면 아래와 같이 쓰지도 않은 b가 들어가있는 반쪽짜리 모듈화가 된 셈이다. 아마 이게 ES2015의 Native Import가 아닌 CommonJS 스타일?인 것으로 알고 있다.
이 챕터는 다소 문제가 많다. 서드파티마다 모듈화 한 방식이 제각각이라 트리 쉐이킹 하는 방법이 다양하고, 나도 처음 접하다 보니 모든 서드 파티를 테스트 할 수가 없어서 대표적으로 react-router(4는 너무 변경 사항이 많아서 3) 요 놈만 건드려보았다.
react-router를 사용하기 위해 react를 설치해야 하고, react를 사용하려면 react-dom도 설치해야하고, 또한 react를 쓰기 위해선 babel-preset-react를 설치해야하는데, babel-preset-react를 쓰기 위해선 babel-core도 설치해야하고, webpack에서 bable을 사용하기 위해선 bable-loader도 설치해야 하고, uglifyJS2가 ES2015를 완벽하게 지원하지 않아서 babel-preset-2015(여기선 babel-preset-env)를 설치해야한다. 위에 절차가 복잡하므로 이해하지 말고 그냥 설치해버리자.
1 2
npm i -S react react-dom react-router@^3.x npm i -D babel-core babel-preset-env babel-preset-react babel-loader
module.exports = { entry: './app.js', output: { filename: 'bundle.js', path: `./`, }, plugins: [ new webpack.optimize.UglifyJsPlugin({ // 사실 아래와 같이만 써도 트리 쉐이킹이 된다. // compress: true
compress: { // warnings: false, // 콘솔 창에 출력되는 게 보기 귀찮다면 요 놈을 주석 제거를 하면 된다. unused: true// 요 놈이 핵심 }, mangle: false, // DEMO ONLY: Don't change variable names.(난독화) beautify: true, // DEMO ONLY: Preserve whitespace (가독성 좋게 함) output: { comments: true// DEMO ONLY: Helpful comments (주석 삭제 안 함) } }),
// 여기서부터 추가된 내용. // 로더들에게 옵션을 넣어주는 플러그인이다. new webpack.LoaderOptionsPlugin({ minimize: true, }), ], module: { // 웹팩 1에서는 loaders를 썼지만 2에선 rules rules: [ { test: /\.js$/, exclude: /node_modules/, // 쿼리가 필요한 로더는 loader로 써줘야함. // 쿼리가 필요 없는 로더는 use로 써도 된다. // 웹팩 2에선 babel-loader와 같이 -loader 생략이 불가능해졌다. loader: 'babel-loader', // babel-loader?머시기와 같은 쿼리나 query: 옵션 대신에 // 웹팩 2에선 options: 로 바뀜. // .babelrc로 따로 빼줘도 상관 없다. options: { presets: [ [ "env", { browsers: ['last 2 versions', '> 10%', 'ie 9'], // babel-preset-2015에서는 Native Module을 쓰지 않는 것인지 // 아래 옵션을 주지 않으면 우리가 만든 코드(ES2015의 import/export Syntax)가 // 트리 쉐이킹 되질 않는다. "modules": false } ], "react" ] } } ] } };
// DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic, see https://github.com/webpack/loader-utils/issues/56 // parseQuery() will be replaced with getOptions() in the next major version of loader-utils. // 위와 같이 로더 개발자를 위한 로그가 뜨는데 보기 싫다면 주석을 제거하면 된다. // process.noDeprecation = true;
이제 번들링 된 파일을 보면 우리가 import 시키지도 않은 browserHistory가 들어있다.
이렇듯 서드 파티들은 트리 쉐이킹이 제대로 되지 않는다. 아마 서드파티 제작자들도 우리가 쓴 ES2015의 import/export 문법을 썼지만 배포할 때는 babel-preset-2015의 Native가 아닌 모듈로 트랜스파일 된 놈이 배포되기 때문에 그런 게 아닐까 싶다. 따라서 아래와 같이 별개의 모듈일 일일이 불러오는 번거로운 작업을 해줘야한다.