컴알못 블로그 About

Webpack + Gulp를 이용한 JavaScript 번들링

Webpack logo image


작년에 React를 공부하면서 Webpack을 처음 접하게 됐습니다. React는 그 자체 보다도 React에 엮인 기술이 정말 많아서 머리가 아프더군요. 최근의 웹 프론트엔드는 기술이 정말 쏟아져 나오는 것 같습니다. 처음엔 Webpack을 공부하면서 정말 많이 헤맸지만 조금 익숙해지고 나니 그 편리함을 깨닫고 회사에서 진행하고 있던 프로젝트에도 전부 적용시켰습니다. 그래서 간단한 Webpack 소개와 함께 제 설정을 소개드리려 합니다.


Webpack이란

Webpack은 자바스크립트의 모듈링을 도와주는 모듈 번들러입니다. 특징으로는 1) CommonJS와 AMD 모듈링 방식을 모두 지원하며, 2) 기존의 다른 번들러들보다 빠르고, 3) 여러가지 리소스를 처리할 수 있는 강력한 로더 기능과 4) 유용한 플러그인들 많다는 점이 있습니다.


Webpack 시작하기

좋은 튜토리얼들이 많이 있으니 같은 내용을 다시 쓰기보단 링크를 소개해드리겠습니다.

Webpack은 빠른 속도로 업데이트 되고 있기 때문에, 기존에 쓰여진 글들은 최신 버전과 다를 수 있어 Webpack의 공식 도큐먼트 위주로 공부하시면 될 것 같습니다.

영문 자료

한글 자료


제가 사용하는 Gulp + Webpack 설정 소개

사용자마다 Webpack config를 작성하는 스타일이 조금씩 다릅니다. 저는 Webpack 환경을 이렇게 구성합니다.

(이 설정은 Webpack을 처음 접하는 분이 이해하기엔 어려움이 있습니다. 공식 도큐먼트를 먼저 읽고 보시는걸 추천드립니다.)

/* webpack.config.js */

var path = require('path');
var webpack = require('webpack');

var PATHS = {
  mymodule: path.join(__dirname, 'mymodule/mymodule.js'),
  build: path.join(__dirname, 'dist/')
};
var PORT = 8300;

module.exports = {
  paths: PATHS,
  port: PORT,
  context: __dirname,
  entry: {
    mymodule: PATHS.mymodule
  },
  output: {
    path: PATHS.build,
    filename: '[name].bundle.js'
  },
  resolve: {
    extensions: ['', '.js', '.json'],
    // require('xxx.js')가 아니라 require('xxx')로 로드할 수 있습니다.
    root: [
      path.resolve("./bower_components")
      // bower_components를 root로 인식하도록 설정했습니다.
      // 상대경로로 bower_components까지 접근해서 로드하지 않아도 됩니다.
    ],
    alias: {
      'fabric': 'fabric.js/dist/fabric.min.js',
      'jquery': 'jquery/dist/jquery.min.js',
      'underscore': 'underscore/underscore-min.js'
      // 쉽게 로드하기 위해 미리 위치를 정의해뒀습니다.
    }
  }
};
/* gulpfile.js */

var gulp = require('gulp');
var gutil = require('gulp-util');
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var merge = require('webpack-merge');
var WebpackStrip = require('strip-loader');
var webserver = require('gulp-webserver');
var del = require('del');

// 공통 Config
var webpackConfig = require('./webpack.config');

// 번들링된 결과를 복사합니다. 저는 모듈이 여러 개 있어서 상위 폴더의 dist에 다 복사했습니다.
gulp.task('copy', ['webpack:build'], function() {
    gulp.src('./dist/mymodule.min.bundle.js')
    .pipe(gulp.dest('../dist/'))
});

// Development
gulp.task('default', ['webpack-dev-server']);

gulp.task('webpack-dev-server', function() {
  var config = merge(webpackConfig, {
    devtool: 'eval',
    entry: {
      mymodule: [
        'webpack-dev-server/client?http://0.0.0.0:' + webpackConfig.port,
        'webpack/hot/only-dev-server',
        webpackConfig.paths.mymodule
      ]
    },
    output: {
      path: webpackConfig.paths.build,
      filename: '[name].bundle.js',
      publicPath: 'http://localhost:' + webpackConfig.port + '/'
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
    ]
  });

  new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    historyApiFallback: true
  }).listen(config.port, '0.0.0.0', function(err, result) {
    if (err) {
      console.log(err);
    }
  });
});

// Production
gulp.task('build', ['clean', 'webpack:build', 'webserver', 'copy']);

gulp.task('webpack:build', ['clean'], function(callback) {
  var config = merge(webpackConfig, {
    entry: {
      'mymodule.min': webpackConfig.paths.mymodule
    },
    module: {

      loaders: [
        {test: /\.js$/, loader: WebpackStrip.loader('debug', 'console.log')}
      ]
    },
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        test: /\.min\.bundle\.js$/,
        sourceMap: false
      })
    ]
  });

  // run webpack
  webpack(config, function(err, stats) {
    if(err) throw new gutil.PluginError('webpack:build', err);
    gutil.log('[webpack:build]', stats.toString({
      colors: true
    }));
    callback();
  });
});

// 빌드가 끝나면 index.html을 열어서 버그 없이 잘 번들링이 됐나 확인합니다.
gulp.task('webserver', ['webpack:build'], function() {
  gulp.src('.')
    .pipe(webserver({
      livereload: true,
      directoryListing: true,
      open: 'index.html',
      port: 8888
    }));
});

gulp.task('clean', function() {
  return del([
    'dist/**/*'
  ]);
});

Commands

gulp (for development)

Hot Reloading을 활성화한 webpack-dev-server를 실행시킵니다.

gulp build (for production)

dist 폴더에 mymodule.bundle.js (번들링), mymodule.bundle.min.js (번들링 + 압축) 파일들을 뽑아냅니다.

저는 webpack.config.js에 Development, Production에 필요한 공통적인 설정을 넣고, gulpfile.js에서 각 레벨에 필요한 추가적인 설정들을 추가합니다. 처음에는 추가할게 별로 없어서 이런식으로 구성했는데, 지금 보니 약간 길어보이긴 하네요. 너무 길게 느껴진다면 webpack.dev.config.js, webpack.prod.config.js에 설정을 빼 두고 merge를 해도 좋습니다.


긴 글 읽어주셔서 감사합니다. 도움이 됐으면 좋겠네요. 잘못된 부분이나, 질문이 있으시면 편하게 댓글 혹은 메일 남겨주세요 :)

comments powered by Disqus