Webpack 정리

2018-04-19

Webpack 은 무엇인가?

  • 서로 연관 관계가 있는 웹 자원들을 js, css, img 와 같은 스태틱한 자원으로 변환해주는 모듈 번들러

WhatIsWebpack

Webpack 을 사용하는 이유 & 배경

  • 새로운 형태의 Web Task Manager
    • 기존 Web Task Manager (Gulp, Grunt)의 기능 + 모듈 의존성 관리
    • 예) minification 을 webpack default cli 로 실행 가능 webpack -p
  • 자바스크립트 Code based Modules 관리
    • 자바스크립트 모듈화의 필요성: AMD, Commong.js ES6(Modules)
    • 기존 모듈 로더들과의 차이점: 모듈 간의 관계를 청크(chunk) 단위로 나눠 필요할 때 로딩
    • 현대의 웹에서 JS 역할이 커짐에 따라, Client Side에 들어가는 코드량이 많아지고 복잡해짐
    • 복잡한 웹 앱을 관리하기 위해 모듈 단위로 관리하는 Common.js, AMD, ES6 Modules 등이 등장
    • 가독성이나 다수 모듈 미병행 처리등의 약점을 보완하기 위해 Webpack 등장

자바스크립트 모듈화 문제란?

<!-- script 태그로 간단히 자바스크립트 모듈화를 시도 하는 예제 -->
<script src="module1.js"></script>
<script src="library1.js"></script>
<script src="module2.js"></script>
<script src="module3.js"></script>
  • 상기 모듈 로딩 방식의 문제점: 전역변수 충돌, 스크립트 로딩 순서, 복잡도에 따른 관리 문제
  • 이를 해결하기 위해 모듈로더들과 웹팩 등장

Webpack 의 철학

  • Everything is Module
  • 모든 웹 자원(js, css, html)은 모듈 형태로 로딩 가능
require('base.css');
require('main.js');
  • Load only ‘what’ you need and ‘when’ you need
    • 필요할 때 필요한 것만 로딩하여 사용

Webpack Entty

  • webpack 으로 묶은 모든 라이브러리들을 로딩할 시작점 설정
  • a, b, c라이브러리를 모두 번들링한 bundle.js 를 로딩한다.
  • 1개 또는 2개 이상의 엔트리 포인트를 설정할 수 있다.
// entry 설정 예
{
  entry: './public/src/index.js' // String

  entry: ['./public/src/index.js'] // Array

  entry: {
      index: './public/src/index.js' // Object
  }
}

// Entry 유형
var config = {
  //  #1 - 간단한 entry 설정
  entry: './path/to/my/entry/file.js'

  // #2 - 앱 로직용, 외부 라이브러리용
  entry: {
    app: './src/app.js'
    vendors: './src/vendors.js'
  }

  // #3 - 페이지당 불러오는 js 설정
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
}

Multiple Entryt Points

  • 복수개 엔트리 포인트에 대한 설정 예시
// webpack.config.js
module.exports = {
  entry: {
    Profile: './profile.js',
    Feed: './feed.js'
  },
  output: {
    path: '[name].js' // 위에 지정한 entry 키의 이름에 맞춰서 결과 산출
  }
}
// 번들파일 Profile.js 를 <script src="build/Profile.js"></script>로 HTML에 삽입

Webpack output

  • entry 에서 설정하고 묶은 파일의 결과값을 설정
var path = require('path');
module.exports = {
  entry: {
    // ...
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
    // filename: '[name].js'
  }
}

Output Name Options

output: {
  filename: '[name].js' // 1
  filename: '[hash].js' // 2
  filename: '[chunkhash].js' // 3
}

참고 API

  • path.join()
    • 해당 API가 동작되는 OS의 파일 구분자를 이용하여 파일 위치를 조합.
path.join('/foo', 'bar', 'baz/asdf');
//  retrun: '/foo/bar/baz/asdf'
  • path.resolve()
    • join()의 경우 문자열만 합치지만, resolve()의 경우 으론쪽에서 왼쪽으로 파일 위치를 구성해가며 유효한 위치를 찾는다.
    • 만약 결과 값이 유효하지 않으면 현재 디렉토리가 사용된다. 반환되는 위치 값은 항상 absolute URL이다.
path.resolve('/foo/bar', './baz');
// return: 'foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// return: '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// if the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'

Webpack Loader

  • 웹팩은 자바스크립트 파일만 처리 가능하도록 되어 있다.
  • loader를 이용하여 다른 형태의 웹 자원들을(img, css, …) js로 변환하여 로딩
module.exports ={
  entry: {},
  output: {},
  module: {
    rules: [
      { test: /\.css$/ , use: ['style-loader', 'css-loader'] }
    ]
  }
}
  • loader 에서 모듈 로딩 순서는 배열의 요소 오른쪽에서 왼쪽으로 진행된다.
{
  test: /backbone/,
  use: [
    'expose-loader?Backbone',
    'imports-loader?+=underscore,jquery'
    // 순서대로 1 jquery, 2 underscore 로딩
  ]
}
  • 위 설정파일을 webpack으로 번들링한 결과
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;
var _ = __webpack_require__(0);
var jquery = __webpack_require(1);

Babel Loader - ES6

  • preset: Babel 플러그인 리스트
module: {
  reles: [{
    test: /\.js$/,
    use: [{
      loader: 'babel-loader',
      options: {
        presets: [
          ['es2015', 'react', {modules: false}] // Tree shaking: 모듈에서 사용되 않으면 추가하지 않음
        ]
      }
    }]
  }]
}

Webpack resolve

  • Webpack 의 모듈 번들링 관점에서 봤을 때, 모듈 간의 의존성을 고려하여 모듈을 로딩해야 한다.
  • 따라서, 모듈을 어떤 위치에서 어떻게 로딩할지에 관해 정의를 하는 것이 바로 Module Resolution
// 일반적인 모듈 로딩 방식
import foo from 'path/to/module'

// 또는
require('path/to/module');
  1. 절대경로를 이용한 파일 로딩
    • 파일의 경로를 모두 입력해준다.
      import '/home/me/file';
      import 'C:\\User\\me\\file';
      
  2. 상대경로를 이용한 파일로딩
    • 해당 모듈이 로딩되는 시점의 위치에 기반하여, 상대 경로를 절대 경로로 인식하여 로딩.
      import '../src/file1'
      import './file2'
      

Resolve Optioin

  • Config 파일에 resolve 를 추가하여 모듈 로딩에 관련된 옵션 사용
  • alias
    • 특정 모듈을 로딩할때 alias 옵션을 이용하면 별칭으로 더 쉽게 로딩이 가능.
    alias: {
      Utilities: path.resolve(__dirname, 'src/path/utilities/')
    }
    
    import Utility from '../../src/path/utilities/utility';
    // alias 사용시 '/src/path/utilities/' 대신 'Utilities' 사용
    import Utility from 'Utilities/utility';
    
    
  • Modules
    • require() import '' 등의 모듈 로딩시에 어느 폴더를 기준할 것인지 정하는 옵션
modules: [node_modules] // default
modules: [path.resolve(__dirname, 'src'), 'node_modules'] // src/node_modules

webpack 빌드를 위한 개발 서버 구성

  • webpack-dev-server : webpack 자체에서 제공하는 개발 서버이고 빠른 리로딩 기능 제공
  • webpack-dev-middleware : 서버가 이미 구성된 경우에는 webpack 을 미들웨어로 구성하여 서버와 연결

Webpack Dev Server

  • 페이지 자동고침을 제공하는 webpack 개발용 node.js 서버