본문 바로가기
Font-end/Webpack

[Webpack4] 무작정 시작하기 (1) - Webpack이란?

by 허도치 2020. 11. 18.
Webpack이란?

  javascript 모듈(Module) 번들러(Bundler)

 

  Webpack은 JS 파일 뿐만 아니라, CSS, HTML, Image 등을 모듈로 로드해서 사용할 수 있다. 또한, 코드를 압축, 최적화 하는 기능들을 제공하며, 번들링된 파일이 너무 무거워질 경우 다시 여러 개의 파일로 나눌 수 있는 코드 스플리팅(Code Spliting) 기능도 제공한다.

 

  핵심 개념으로는 Mode, Entry, Output, Loader, Plugin가 있으며, 추가로 Optimization이 있다.

 

 

모듈(Module)이란?

  모듈은 프로그램을 구성하는 요소 중 하나이며 관련된 데이터와 함수들이 묶여서 모듈을 형성하고, 주로 파일 단위로 관리된다.

 

  예를 들어, 팝업은 isOpen이라는 상태 데이터open, close, destroy와 같은 함수를 가진 하나의 모듈이며, 로그인은 logedIn이라는 상태 데이터doLogin, checkValidation과 같은 함수를 가진 모듈이다. 이 두 모듈은 각각의 Javascript 파일에 작성된다.

 

  이렇게 모듈화된 프로그래밍은 유지보수가 쉬워지는 장점이 있다.

 

 

번들러(Bundler)란?

  번들러는 모듈별로 나누어진 파일을 특정 단위로 묶어서 요청에 대한 응답으로 전달할 수 있는 환경을 만들어주는 역할을 한다. 모듈 또는 외부 라이브러리 간의 의존성을 쉽게 관리할 수 있다.

 

  예를 들어, A, B, C, ..., Z 라는 모듈들이 있을 때, 이를 알파벳이라는 하나의 번들로 묶을 수 있다. 만약, A가 a라는 라이브러리를 사용하고 있다면, a라는 라이브러리도 함께 번들링된다.

 

  번들링된 파일을 열어보면 A, B, C, ..., Z와 a가 모두 포함된 내용이 압축되어있다.

 

 

번들링을 하는 이유

  웹페이지에서 모듈을 사용하려면 해당 모듈과 모듈에서 사용되는 라이브러리들을 로드해야하는데, 선후 관계를 따져서 순서대로 로드해야 한다. 몇 개의 모듈을 사용한다면 <script> 태그를 이용하여 간단하게 로드 할 수 있겠지만, 만약, 수십, 수백 개의 모듈을 사용해야 한다면 매우 복잡해질 것이다. 또한, 이렇게 많은 양의 파일을 한번에 로드 할 경우 네트워크의 병목현상이 발생할 수 있다. 병목현상은 하나의 Javascript 파일에 최대한 많은 내용을 작성하면 해결 할 수 있지만, 유지보수와 가독성이 떨어진다.

 

  이 때, 번들링된 파일을 로드하면 위와 같은 문제들을 해결 할 수 있다. 모듈을 번들링하는 과정에서 모듈이나 외부 라이브러리의 의존성들을 로드하기 때문에 선후 관계를 따질 필요 없으며, 여러 파일로 작성된 모듈들을 특정 단위로 묶어서 하나의 파일로 만들기 때문에 병목현상도 예방 할 수 있다.

 

 

 

핵심개념
Mode

  배포 환경 설정

 

  mode는 development와 production, none을 선택할 수 있다. 기본값은 production이며, 이는 배포용이고 최적화가 자동으로 적용된다. devlopment는 개발용이며 webpack dev server를 사용할 수 있다.

 

1
2
3
4
5
6
7
// Usage
mode: <string> // development, production, none
 
// Example
module.exports = {
  mode: 'development',
}
cs

 

 

Entry

  번들링 시작 모듈의 경로 설정

 

  Entry는 내부적으로 종속성에 대한 그래프를 작성하기위한 시작 모듈을 설정하는 것으로 해당 모듈이 의존하는 모든 모듈과 라이브러리를 파악한다.

 

  Entry를 작성하는 방법에는 Single과 Multiple Entry 두가지가 있다. Single Entry는 지정한 Entry Point들을 한개의 번들로 만드는 것이며, Multiple Entry는 이름(name)과 콘텐츠 해시(contentHash) 가진 여러개의 번들로 만들 수 있다. 이름과 콘텐츠 해시는 Output에서 사용된다.

 

  4버전 이전에는 Entry에 bootstrap, react와 같은 vendor를 별도의 Entry Point로 추가하고 CommonsChunkPlugin을 사용하여 번들링하는 것이 일반적이었느나, 4버전 이후에는 권장하지 않는다. 대신, optimization.splitChunks을 통해 app과 vendor를 분리하는 것을 권장한다.

 

1
2
3
4
5
6
7
8
9
// Single Entry (Shorthand) Syntax
 
// Usage
entry: <String> | [<String>]
 
// Example
module.exports = {
  entry: './path/to/my/entry/file.js'
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Multiple Entry (Object) Syntax
 
// Usage
entry: {
  '<entryChunkName>'<String>| [<String>]
}
 
// Example
module.exports = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }
}
cs

 

 

Output

  번들링 결과 출력 설정

 

  의미 그대로 번들링된 결과를 출력할 파일명과 경로을 설정할 수 있다. 기본 경로는 **'./dist'**이며, 해당 경로에 지정한 파일명으로 번들 파일이 생성된다. 만약, Multiple Entry로 설정되어있다면 Entry에서 설정한 이름으로 출력 파일명을 동적으로 할당할 수 있으며, 콘텐츠 해시를 파일명에 추가하여 파일명 중복을 방지할 수 있다.

 

  또한, publicPath 옵션을 사용할 수 있는데, 브라우저에서 참조될때 출력 파일의 공용 URL 주소로 사용된다. 다른 도메인이나 CDN에서 일부 또는 모든 출력 파일을 호스팅하려는 경우 사용할 수 있으며, Webpack Dev Server에서는 출력파일의 URL 경로로 사용된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Usage
output: {
  filename: <String>,
  path: <String>,
  publicPath: <String>
}
 
// Example
// Single Entry Points
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: __dirname + '/dist',
    publicPath: '/public/',
  }
};
 
 
// Example
// Multiple Entry Points
module.exports = {
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist',
    publicPath: '/public/',
  }
};
// output -> [ ./dist/app.js, ./dist/search.js ]
 
 
// Example
// CDN for assest 
module.exports = {
  //...
  output: {
    path: '/home/proj/cdn/assets/[hash]',
    publicPath: 'https://cdn.example.com/assets/[hash]/'
  }
};
cs

 

 

Loaders

  다양한 유형의 파일을 모듈화

 

  기본적으로 Webpack은 Javascript와 JSON 파일만 처리할 수 있다. Loader는 Webpack이 다른 유형의 파일을 처리하고 이를 응용 프로그램이 소비하고 종속성 그래프에 추가 할 수있는 유효한 모듈로 변환하는 기능을 한다. 예를들어, CSS 파일을 Loader로 모듈화하여 Javascript 내에서 로드하여 사용할 수 있다.

 

  Loader는 **'test'**와 **'use'**라는 주요 옵션이 있다. test는 정규표현식으로 작성되며, 변환할 대상을 식별하는데 사용되고, use는 변환하는데 사용되는 Loader를 지정한다. Webpack이 컴파일하면서 Javascript 내에 require 또는 import로 로드된 파일을 식별하여 번들링되기 전에 변환을 수행한다. 또한, use에 Loader를 여러개 지정할 수 있는데, 이는 아래에서 위로 우선순위를 갖는다.

 

  구버전의 브라우저에서는 최신 Javascript 문법(const, let, arrow function 등)을 지원하지 않을 수 있다. 이 때, babel-loader를 사용하면 최신 문법으로 작성된 Javascript를 모든 브라우저에서 해석할 수 있도록 변환할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Usage
module: {
  rules: [
    { 
          test: <RegularExpression>
            use: <String>
        }
  ]
}
 
 
// Install Loaders
// for css
npm install --save-dev css-loader style-loader sass-loader
 
// for babel
npm install --save-dev babel-loader @babel/core @babel/preset-env
 
 
// Example
// webpack.config.js
module.exports = {
  module: {
    rules: [
      { // .css로 끝나는 파일은 'css-loader'로 모듈화
                test: /\.css$/
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader'
            options: {
              modules: true
            }
          },
          { loader: 'sass-loader' },
                ]
            },
            { 
                test: /\.js$/,
                exclude: /node_modules/
                loader: "babel-loader"
            }
    ]
  }
};
 
// Example
// .babelrc
{
  "presets": ["@babel/preset-env"]
}
cs

 

 

Plugins

  다양한 Webpack 빌드 프로세스 설정

 

  Webpack으로 빌드를 할 때 사용할 수 있는 추가적인 기능들을 제공하여, 다양한 방법의 빌드 환경을 구성할 수 있다. Webpack 패키지에 포함된 내장 플러그인 외에 추가로 필요한 기능은 외부 플러그인을 받아서 사용할 수 있다.

 

  플러그인에 따라 사용법이 다르기 때문에 사용법과 예제는 다른 포스트에서 다루어 보도록 하겠다.

 

  내장플러그인 목록

 

Internal webpack plugins | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

 

  외부플러그인 목록

 

Plugins | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

 

 

Optimization

  빌드 최적화 설정

 

  번들 파일은 여러 모듈들이 한 개의 파일에 작성된 것으로 크기가 매우 크다. optimization은 이러한 번들 파일의 크기를 최대한 줄이거나 분할하는 등의 최적화 작업을 하는 역할을 한다.

 

  minimize 옵션은 Javascript의 크기를 축소 시키는 옵션으로 jquery.min.js처럼 공백과 네이밍을 최소화시킨다. production 모드에서는 기본값으로 true이다.

 

  최적화 옵션 목록

 

Optimization | webpack

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

webpack.js.org

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Example
module.exports = {                           
    ...
    optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    minimize: true,
  }
    ...
};
cs

 

 

 

예제
install loader
1
npm install --save-dev babel-loader
cs

 

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const webpack = require("webpack");
const path = require("path");
 
module.exports = {
  mode: "development",
  entry: {
    app: [
      path.join(__dirname, "../js/index.js"),
    ]
  },
  output: {
    filename: "[name].bundle.js",
    path: path.join(__dirname, "../dist"),
    publicPath: "/public/",
  },
  plugins: [
    new webpack.ProgressPlugin((percentage, message, ...args)=>{
      console.info(Math.trunc(percentage*100), "%", message, ...args);
    }),
  ],
  module: { // loaders
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      }
    ]
  },
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
  },
}
 
cs

 

 

 

댓글