plugin란?
파일단위로 처리하는 loader와 다르게 플러그인은 번들링된 결과를 처리합니다. 번들링된 자바스크립트를 난독화하거나 텍스트를 추출하는 등의 용도로 사용합니다.
플러그인을 빌드하는 것은 로더를 빌드하는 것보다 조금 더 고급입니다. 플러그인을 이해하려면 webpack 저수준 내부의 이해가 필요합니다.
이해를 위해 플러그인을 직접 만들어보고 내부적으로 어떻게 작동하는지 알아보겠습니다!
plugin 만들기
- 
plugin은apply메소드가 있는javascript객체입니다. - 
apply메서드는 웹팩 컴파일러에 의해 한 번 호출되어 전체 컴파일에 대한 엑서스를 제공합니다. 
   class HelloWorldPlugin {
    apply(compiler) {
        compiler.hooks.done.tap(
        'Hello World Plugin',
        (
            stats /* stats is passed as an argument when done hook is tapped.  */
        ) => {
            console.log('Hello World!');
        }
        );
    }
    }
    module.exports = HelloWorldPlugin;
    const MyPlugin = require("./myplugin")
    module.exports = {
        ...
        plugins: [new MyPlugin()],
    }
build 결과 :
- 맨위에 
Hellow World!log가 찍힌 것을 확인할 수 있습니다.> npm run build > webpack-demo@1.0.0 build > webpack Hello World! asset images/bg.jpg 612 KiB [emitted] [from: src/bg.jpg] (auxiliary name: main) asset main.js 33.8 KiB [emitted] (name: main) runtime modules 2.39 KiB 7 modules javascript modules 10.8 KiB modules by path ./node_modules/ 8.66 KiB modules by path ./node_modules/style-loader/dist/runtime/*.js 5.75 KiB 6 modules modules by path ./node_modules/css-loader/dist/runtime/*.js 2.91 KiB 3 modules modules by path ./src/ 2.19 KiB modules by path ./src/*.js 323 bytes 2 modules modules by path ./src/*.css 1.88 KiB ./src/app.css 1.11 KiB [built] [code generated] ./node_modules/css-loader/dist/cjs.js!./src/app.css 785 bytes [built] [code generated] asset modules 5.17 KiB (javascript) 612 KiB (asset) ./src/webpack.png 5.13 KiB [built] [code generated] ./src/bg.jpg 42 bytes (javascript) 612 KiB (asset) [built] [code generated] webpack 5.65.0 compiled successfully in 867 ms 
여기서 중요한 건 loader는 파일 수 만큼 log가 찍혔다면 plugin은 한 번만 찍힌 것을 확인할 수 있습니다.
위에 설명대로 plugin은 하나로 번들링된 결과에 대해서 처리하기 때문입니다.
번들링된 결과(파일) 접근
- 비동기 이벤트 후크 : 
tapAsync - 
tapAsync메소드를 사용하여 플로그인을 사용할 때는 함수의 마지막 인수로 제공되는 콜백 함수를 호출해야 합니다. - 
compiler모듈은CLI또는Node API를 통해 전달된 모든 옵션으로 컴파일 인스턴스를 생성하는 메인 엔진입니다. - 
Compilation모듈은compiler에서 새 컴파일 또는 빌드를 만드는데 사용합니다.Compilation객체는는 모든 모듈과 디펜던시에 접근할 수 있습니다. 
compiler에 의해 번들링된 결과가 compilation 인자로 들어오면 compilation 객체로 청크를 복원하여 파일 소스를 콘솔에 찍습니다.
   class MyPlugin {
        apply(compiler) {
            compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
            // 각 청크를 탐색합니다.
            compilation.chunks.forEach((chunk) => {
                // 청크에 의해 생성된 각 asset 파일 이름을 탐색합니다.
                chunk.files.forEach((filename) => {
                // 청크에 의해 생성된 각 파일의 asset 소스를 가져옵니다.
                var source = compilation.assets['main.js'].source();
                console.log(source);
                });
            });
            callback();
            });
        }
    }
  module.exports = MyPlugin;
build 결과 : log 상단의 주석과 번들링 결과인 main.js의 상단의 주석이 같은 것을 확인할 수 있습니다.


BannerPlugin
- 생성된 각 
chunk상단에 정보들을 추가 입력할 수 있게 도와주는 플러그인입니다. - 예) 빌드 버전, 커밋 버전, 개발자명, 참여인원, 수정날짜 등
 
BannerPlugin :
    const webpack = require('webpack');
    new webpack.BannerPlugin(banner);
    // or
    new webpack.BannerPlugin(options);
Options :
    {
        banner: string | function, // the banner as string or function, it will be wrapped in a comment
        raw: boolean, // if true, banner will not be wrapped in a comment
        entryOnly: boolean, // if true, the banner will only be added to the entry chunks
        test: string | RegExp | [string, RegExp], // Include all modules that pass test assertion.
        include: string | RegExp | [string, RegExp], // Include all modules matching any of these conditions.
        exclude: string | RegExp | [string, RegExp], // Exclude all modules matching any of these conditions.
    }
사용방법 :
    import webpack from 'webpack';
    // string
    new webpack.BannerPlugin({
        banner: 'hello world',
    });
    // function
    new webpack.BannerPlugin({
        banner: (yourVariable) => {
            return `yourVariable: ${yourVariable}`;
        },
    });
    Place
배너 파일 생성 :
- 배너 정보가 많을 경우에는 파일을 따로 생성해서 적용할 수 있습니다.
 
    const webpack  = require('webpack');
    const banner = require('./banner.js');
    module.exports = {
        ...
        plugins : [new webpack.BannerPlugin(banner)],
    }
    const childProcess = require("child_process");
    module.exports = function banner() {
        const user_name = childProcess.execSync("git config user.name");
        const date = new Date().toLocaleString();
        const commit = childProcess.execSync("git rev-parse --short HEAD")
        const node = childProcess.execSync("node --version")
        return (
            `
                user name : ${user_name}
            `+
            `
                Build date : ${date}
            `+
            `
                Commit version : ${commit}
            `+
            `
                node.js version : ${node}
            `
        )
        
    }
build 결과 :

DefinePlugin
- 
DefinePlugin을 사용하면 컴파일 타임에 구성할 수 있는 전역 상수를 만들 수 있습니다. - 
DefinePlugin은 개발환경, 운영환경에 따라 다른 동작을 하고 싶을 때 사용합니다. - 가령, 개발환경에서는 로깅을 수행하지만, 운영환경에서는 수행하지 않는 경우에 전역 상수를 사용하여 로깅의 수행여부를 결정할 수 있습니다.
 - 
DefinePlugin을 사용하여 개발 및 운영 빌드 환경을 잊어버리시면 됩니다. - 참고로, 
process.env.NODE_ENV는 노드 환경정보가 들어있는 변수이다. 처음webpack설정의mode값이 들어가 있다. 
    const webpack  = require('webpack');
    module.exports = {
        ...
        plugins : [
            new webpack.DefinePlugin({
                PRODUCTION: JSON.stringify(true),
                VERSION: JSON.stringify('5fa3b9'),
                BROWSER_SUPPORTS_HTML5: true,
                TWO: '1+1',
                'typeof window': JSON.stringify('object')
            })
        ],
    }
    
    ...
    // DefinePlugin에서 구성한 전역 상수를 사용할 수 있다.
    console.log(PRODUCTION);
    console.log(VERSION);
    console.log(BROWSER_SUPPORTS_HTML5);
    console.log(TWO);
    console.log(typeof window);
    console.log(process.env.NODE_ENV);
build 결과 :

CleanWebpackPlugin
- 
CleanWebpackPlugin은build된 폴더를 제거/정리하는webpack plugin입니다. - 
Node v10+및webpack v4+에서 지원됩니다. - 기본적으로 이 플러그인은 
output.path성공적인 재구축 후webpack디렉토리 내의 모든 파일과 사용되지 않는webpack자산들을 모두 제거합니다. 
install :
    npm install --save-dev clean-webpack-plugin
    // default export가 아니기 때문에 { name } 으로 불러옵니다.
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    module.exports = {
        ...
        plugins : [
            new CleanWebpackPlugin(),
        ],
    }
dist 폴더에 test.js 추가 :

build 결과 :
    npm run build
    > webpack-demo@1.0.0 build
    > webpack
    asset images/bg.jpg 612 KiB [emitted] [from: src/bg.jpg] (auxiliary name: main)
    asset main.js 34.1 KiB [emitted] (name: main)
    runtime modules 2.39 KiB 7 modules
    javascript modules 11 KiB
    modules by path ./node_modules/ 8.66 KiB
        modules by path ./node_modules/style-loader/dist/runtime/*.js 5.75 KiB 6 modules
        modules by path ./node_modules/css-loader/dist/runtime/*.js 2.91 KiB 3 modules
    modules by path ./src/ 2.36 KiB
        modules by path ./src/*.js 494 bytes 2 modules
        modules by path ./src/*.css 1.88 KiB
        ./src/app.css 1.11 KiB [built] [code generated]
        ./node_modules/css-loader/dist/cjs.js!./src/app.css 785 bytes [built] [code generated]
    asset modules 5.17 KiB (javascript) 612 KiB (asset)
    ./src/webpack.png 5.13 KiB [built] [code generated]
    ./src/bg.jpg 42 bytes (javascript) 612 KiB (asset) [built] [code generated]
    webpack 5.65.0 compiled successfully in 3267 ms
dist 디렉토리를 깔끔하게 지우고 다시 build된 것을 확인할 수 있습니다.

HtmlWebpackPlugin
- 
HtmlWebpackPlugin은HTML파일 생성을 단순화합니다. 빌드 타임에 값을 넣거나 코드를 압축할 수 있습니다. - 
webpack.config.js에서 설정한output path에dist/index.html파일이 생성됩니다. - 
3rd party패키지입니다. 
install :
   npm install --save-dev html-webpack-plugin
./index.html :
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <!-- 스크립트 제거 -->
        <!-- <script src="./dist/main.js"></script> -->
    </body>
    </html>
   const HtmlWebpackPlugin = require('html-webpack-plugin');
   module.exports = {
        ...
        plugins : [
            new HtmlWebpackPlugin(),
        ],
    }
build 결과 :
- 
dist/index.html파일이 생성되었고webpack의 엔트리 포인트는 생성된HTML에 모두<script>태그로 포함됩니다. - 만약 
webpack출력에css asset이 있다면((MiniCssExtractPlugin으로 추출된CSS) 이들은 생성된HTML파일의<head>요소 안에<link>태그로 포함됩니다. 

######## Options를 줘서 커스텀할 수 있습니다.
- HtmlWebpackPlugin Options 참고하기 (click)
 - 
./index.html->src/index.html에 하나 복사합니다. 
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>webpack app <%= env %></title>
    </head>
    <body>
        <!-- <script src="./dist/main.js"></script> -->
    </body>
    </html>
   const HtmlWebpackPlugin = require('html-webpack-plugin');
   module.exports = {
        ...
        plugins : [
            new HtmlWebpackPlugin({
            title: 'siksik webpack',        // 생성된 HTML에 사용할 제목
            filename : 'siksik.html',       // 생성할 HTML 파일명, 기본값은 index.html
            template: './src/index.html',   // 템플릿 경로를 지정
            templateParameters: {           // 템플릿에 사용된 매개변수를 덮어쓸 수 있습니다.
              env: process.env.NODE_ENV === 'development' ? '(development)' : '(product)',
            },
        })
        ],
    }
NODE_ENV=production npm run build 결과 :
   NODE_ENV=production npm run build

NODE_ENV=development npm run build 결과 :
   NODE_ENV=development npm run build

MiniCssExtractPlugin
- 
MiniCssExtractPlugin은CSS를 별도의 파일로 추출합니다.CSS가 포함된 JS파일별로CSS파일을 생성합니다. - 
CSS가 많아 질수록 하나의 Js 결과물로 만드는 것이 부담될 수 있습니다. 번들된 결과에서CSS코드만 뽑아 별도의CSS파일로 만들어 파일을 분리하는게 유리할 수 있습니다. 브라우저에서 큰 파일을 하나 받는 것 보다, 여러 개의 작은 파일을 동시에 다운로드하는게 빠릅니다. - 
css-loader와 함께 사용합니다. - 
production환경인 경우엔MiniCssExtractPlugin.loader를 사용하고development환경인 경우엔style-loader를 사용해보자 - 
MiniCssExtractPlugin.loader는MiniCssExtractPlugin에서 제공하는 로더이다. 
install :
   npm install --save-dev mini-css-extract-plugin
   const MiniCssExtractPlugin = require("mini-css-extract-plugin");
   module.exports = {
       plugins: [new MiniCssExtractPlugin()],
       module: {
           rules: [
               {
                   test: /\.css$/i,
                   use: [
                        process.env.NODE_ENV === 'production'
                        ? MiniCssExtractPlugin.loader
                        : 'style-loader',
                        'css-loader'
                   ]
               },
            ],
        },
        plugins : [
            ...(process.env.NODE_ENV === 'production'
            ? [new MiniCssExtractPlugin({filename: '[name].css'})]
            : []
        ],
    };
    import './app.css';
    ...
app.css :
    body {
        background-image: url(bg.jpg);
    }
NODE_ENV=production npm run build 결과 :
- 
main.css파일이 dist 폴더에 생성된 것을 확인할 수 있습니다. 

- 
HtmlWebpackPlugin에 의해 생성된HTML안에css asset(main.css)이<head>요소 안에<link>태그로 포함된 것을 확인할 수 있습니다. 
