[컴][nodejs] webpack + reactjs 사용하기

webpack / react / jsx / ES6 를 사용한다.



여기서 ReactJS 와 함께 webpack 을 한 번 사용해 보자.

Webpack

이 webpack 은 모듈화를 위한 묶어주는 도구라고 보면 될 듯 하다. 보통 web page 는 html/js/css 로 이루어지는데, 원래 태생이 달라서 대부분 이녀석들을 하나로 묶어서 관리하지는 않았다.

하지만 이제는 web application 이 좀 더 rich 해져서 이 녀석들을 하나로 묶어서 관리할 필요가 더욱 커지고 있다. 여하튼 이런 것들을 java 등에서는 언어자체에서 지원을 해주지만, web 진영에는 언어자체에서 이런 지원을 하는 것이 아니라서 여러가지 tool 들이 나와있다.

그 중에 어쩌면 요새 유행하는 녀석이라고 볼 수 있을 듯 하다. 여하튼 아직까지는 나도 낯설어서 설명이 허접할 것이니 감안하고 보자.



설치

npm init


D:\mine\programming\nodejs\js>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (js) react-ecma6-boilerplate
version: (1.0.0) 0.1.0
...


npm install

d:\..> npm install --save react
d:\..> npm install --save react-dom
d:\..> npm install --save-dev webpack


webpack 에서 babel 을 사용하는 것은 babel-loader 를 통해 가능하다. loader 와 관련된 글을 읽어보자.

d:\..> npm install --save-dev babel-loader babel-core
d:\..> npm install --save-dev babel-preset-es2015 babel-preset-react


tree shaking, babel-preset-es2015-native-modules

참고로 webpack 2 에서 tree shaking 을 제공하는데, 이것을 위해서는 "babel-preset-es2015-native-modules" 를 사용해야 한다.
tree-shaking 을 통해 harmony export 가 결정되는데, 아래처럼 output 이 나오게 된다. 이 때는 export 를 선별해서 하고, export 를 하지 않는 module(unused harmony export)등도 같이 보여진다. 하지만, minimizing step 에서 사용하지 않는 부분을 없애준다.(--optimize-minimize 등)

확실히 필요하지 않은 부분을 없애주는지 확인하려했는데, 내 code 가 한 file 에 여러 component 를 가지고 있어서 그런지, 쓰지 않는 component 를 완전히 없애주지 못하는 듯 보였다. 필요한 부분만 정확히 묶고 싶은 경우라면, 각 component 를 각각 하나의 file 로 분류해야 할 듯 하다.

"use strict";
/* harmony export */ exports["a"] = add;/* harmony export */ exports["b"] = multiply;/* unused harmony export list */function add() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
}




webpack.config.js

webpack.cmd 를 실행하려면 config 를 넣어줘야 한다.
var path = require('path');
var webpack = require('webpack');

var buildPath = 'build/'
var commonsPlugin =
  new webpack.optimize.CommonsChunkPlugin(buildPath + 'common.js');


module.exports = {
  entry: {
    'login': ['./src/js/login.jsx'],
    'login2': ['./src/js/login2.jsx'],
    'vendor' : ['react', 'react-dom'],
  },
  output: { 
    path: __dirname, 
    filename: buildPath + 'app.bundle.[name].js'
    
  },
  // Enable sourcemaps for debugging webpack's output.
  devtool: "source-map",
  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".ts", ".tsx", ".js", ".json"],
    alias:{
    // utils: path.join(__dirname, '/src/js/common/utils'),
       
    }
  },
  module: {
    rules: [
      // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
      { test: /\.tsx?$/, loader: "awesome-typescript-loader" },

      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
    ],
    loaders: [
      {
        //tell webpack to use babel-loader for all *.jsx files
        test: /.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
  // When importing a module whose path matches one of the following, just
  // assume a corresponding global variable exists and use that instead.
  // This is important because it allows us to avoid bundling all of our
  // dependencies, which allows browsers to cache those libraries between builds.
  externals: {
      "react": "React",
      "react-dom": "ReactDOM"
  },
  plugins: [commonsPlugin]
};



build

이제 webpack 을 이용한 build 를 해 보자. 아래처럼 command 를 사용하면 된다.
d:\..> node_modules\.bin\webpack.cmd



file-loader

require 와 연관된 녀석들만 copy 하는 듯 하다.

개인적으로 CopyWebpackPlugin 이 좀 더 직관적인 듯 하다. 그런데 build 가 끝나기전에 copy 를 하는 등의 문제가 있다.(참고: Copied file is empty · Issue #28)
d:\..> npm install --save-dev copy-webpack-plugin



css-loader

css-loader 를 사용하면 css 관리가 용이해 진다. 사용법은 아래를 참고하자.


TypeScript + ReactJS



gulp

아직까지는 build 한 결과를 한 곳에 모아놓고, 이것을 dist 에 copy 하는 작업은 gulp 를 사용해야 할 듯 보인다.

  1. npm install --save-dev gulp gulp-util
  2. // gulpfile.js
    
    var gulp = require("gulp");
    var gutil = require("gulp-util");
    
    var path = require('path');
    var webpack = require("webpack");
    
    var config = require('./webpack.config.js');
    
    
    
    
    gulp.task("webpack", function(callback) {
        // run webpack
        webpack(config).run(function(err, stats) {
            if(err) throw new gutil.PluginError("webpack", err);
            gutil.log("[webpack]", stats.toString({
                // output options
            }));
    
            // copy to dist
            console.log('copy .js files to dist folder.')
            gulp.src([
               './build/*.js'
     ])
            .pipe(gulp.dest('../src/static/js'))
    
            callback();
        });
    
    });
  3. .\node_modules\.bin\gulp.cmd webpack

참고로, optimization option (--optimize-minimize, 등)을 사용했다면, 이녀석은 webpack.config.js 의 plugin 으로 설정해 주면 된다. 아래 문서를 참고하자.



CommonsChunkPlugin

common chucks 를 만들어주는 plugin



library 로 묶기


webpack 을 이용하면, 기본적으로 자신들이 알아서 이름을 변경해서 사용한다. 그래서 이것을 그냥 <script> 로 include 해서 가져다 쓸 수 없다. 이를 위해서 webpack 의 configuration 에 external 을 설정해 주면 된다.

ref. 5 에 있는 source 로 설명을 하자면,
  • externals 에 있는 부분은 webpack 으로 묶이지 않는다. library 중에 밖으로 노출시킬 녀석을 설정하면 되고,
    • external 에 설정하는 것이 필요한 때는 library 를 bundle.js 로 묶는 경우에 library 중 하나라도 변경되면 bundle.js 가 변경돼서 browser 는 cache miss 를 발생시킬 것이다. 하지만 external 로 해놓으면 자신의 library 가 변경되어도 다른 cached library 에 영향을 주지 않게 된다.
  • output.library 는 webpack 으로 만들어진 bundle.js 의 이름을 정해주는 것이라

보면 된다.


var jQuery = require("jquery");
var math = require("math-library");

function Foo() {}

// ...

module.exports = Foo;

module.exports = {
  entry: './src/js/main.js',
  output: { 
    path: __dirname, 
    filename: './build/app.bundle.js',
    
    // export itself to a global var
    libraryTarget: "var",
    // name of the global var: "Foo"
    library: "Foo"
  },
  externals: {
      // require("jquery") is external and available
      //  on the global var jQuery
      "jquery": "jQuery"
  },



Bower 사용

ref. 4

webpack 설정에 2가지를 추가해 줘야 한다. 그런데 대체로 npm  module 를 사용하는 것이 좋다고 webpack 에서 권고한다.

webpack 이 다루기 쉽고, optimize 하기에 npm module 이 좋다고 한다. ref. 4 에 보면 npm 을 이용한것과 bower 를 이용한 것에 대한 compile time 과 bundle size 를 비교해 놨다. 참고하자.

var path = require('path');
var webpack = require('webpack');
 
module.exports = {
  entry: './src/main.js',
  resolve: {
    modulesDirectories : ["web_modules", "node_modules", "bower_components"]
  },
  plugins: [
        new webpack.ResolverPlugin(
            new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"])
        )
    ],
  output: { 
    path: __dirname, 
    filename: './build/bundle.js' 
  },
  module: {
    loaders: [
      {
        //tell webpack to use babel-loader for all *.jsx files
        test: /.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
};

댓글 없음:

댓글 쓰기