LHJ

I'm a FE developer.

자주 사용하는 플러그인 - MiniCssExtractPlugin

01 Oct 2020 » node_webpack2

MiniCssExtractPlugin

스타일시트가 점점 많아지면 하나의 자바스크립트 결과물로 만드는 것이 부담일 수 있다.
번들 결과에서 스타일시트 코드만 뽑아서 별도의 CSS 파일로 만들어 역할에 따라 파일을 분리하는 것이 좋다.
브라우저에서 큰 파일 하나를 내려받는 것보다, 여러개의 작은 파일을 동시에 다운로드하는 것이 더 빠르다.

최종 결과물이 CSS파일 하나, JS파일 하나 이렇게 나오는 것이 좋다.

개발 환경에서는 CSS를 하나의 모듈로 처리해도 상관없지만 production 환경에서는 분리하는 것이 효과적이다.
MiniCssExtractPlugin 은 CSS를 별도 파일로 뽑아내는 플러그인이다.

이 플러그인 또한 써드파티 플러그인이기 때문에 설치해야된다.

npm i -D mini-css-extract-plugin
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const childProcess = require('child_process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js" // name - 위에 main 키네임을 가져온다.
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'url-loader',
                options: {
                    // publicPath: './dist',
                    name: '[name].[ext]?[hash]', // name - 원본파일이름
                    limit: 20000, // 20kb
                }
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin({
            banner: `
                Build Date: ${new Date().toLocaleString()}
                Commit Version: ${childProcess.execSync('git rev-parse --short HEAD')}
                Author: ${childProcess.execSync('git config user.name')}
            `
        }),
        new webpack.DefinePlugin({
            TWO: JSON.stringify('1+1'),
            'api.domain': JSON.stringify('http://dev.api.domain.com')
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            templateParameters: {
                env: process.env.NODE_ENV === 'development' ? '(개발용)' : ''
            },
            minify: process.env.NODE_ENV === 'production' ? {
                collapseWhitespace: true,
                removeComments: true,
            } : false
        }),
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name].css' // 원본파일 이름 (이렇게 설정 안하면 해시값으로 내보냄), 근데 결국 js파일의 원본네임이니까 위의 main 키네임 가져오는 건 같음
        }),
    ]
}

이 플러그인은 자바스크립트에서 CSS를 뽑아내는 과정을 추가하는 것이기 때문에 굳이 개발 환경에서는 안해도 된다. 개발환경에서는 자바스크립트 파일 하나로 빌드하는 것이 좀 더 빨리 빌드될 것이다.

const path = require('path');
const webpack = require('webpack');
const childProcess = require('child_process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js" // name - 위에 main 키네임을 가져온다.
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'url-loader',
                options: {
                    // publicPath: './dist',
                    name: '[name].[ext]?[hash]', // name - 원본파일이름
                    limit: 20000, // 20kb
                }
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin({
            banner: `
                Build Date: ${new Date().toLocaleString()}
                Commit Version: ${childProcess.execSync('git rev-parse --short HEAD')}
                Author: ${childProcess.execSync('git config user.name')}
            `
        }),
        new webpack.DefinePlugin({
            TWO: JSON.stringify('1+1'),
            'api.domain': JSON.stringify('http://dev.api.domain.com')
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            templateParameters: {
                env: process.env.NODE_ENV === 'development' ? '(개발용)' : ''
            },
            minify: process.env.NODE_ENV === 'production' ? {
                collapseWhitespace: true,
                removeComments: true,
            } : false
        }),
        new CleanWebpackPlugin(),
        ...(process.env.NODE_ENV === 'production' ? [
                new MiniCssExtractPlugin({
                    filename: '[name].css' // 원본파일 이름 (이렇게 설정 안하면 해시값으로 내보냄), 근데 결국 js파일의 원본네임이니까 위의 main 키네임 가져오는 건 같음
                }),
            ] : []
        )
    ]
}

... 나머지 연산자로 위와 같이 정의해주면 환경변수에 따라 MiniCssExtractPlugin를 활성화 하거나 활성화를 안 시킬 수 있다.
... 나머지(스프레드) 연산자는 말 그대로 배열을 펼치는 연산자이기 때문에 ...() 소괄호 안에 배열을 넣어줘야된다.
MiniCssExtractPlugin 이 플러그인은 다른 플러그인과 다르게 로더 설정도 추가해야된다.

// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const childProcess = require('child_process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "development",
    entry: {
        main: "./src/app.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js" // name - 위에 main 키네임을 가져온다.
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    process.env.NODE_ENV === 'production' ?
                        MiniCssExtractPlugin.loader :
                        'style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'url-loader',
                options: {
                    // publicPath: './dist',
                    name: '[name].[ext]?[hash]', // name - 원본파일이름
                    limit: 20000, // 20kb
                }
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin({
            banner: `
                Build Date: ${new Date().toLocaleString()}
                Commit Version: ${childProcess.execSync('git rev-parse --short HEAD')}
                Author: ${childProcess.execSync('git config user.name')}
            `
        }),
        new webpack.DefinePlugin({
            TWO: JSON.stringify('1+1'),
            'api.domain': JSON.stringify('http://dev.api.domain.com')
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            templateParameters: {
                env: process.env.NODE_ENV === 'development' ? '(개발용)' : ''
            },
            minify: process.env.NODE_ENV === 'production' ? {
                collapseWhitespace: true,
                removeComments: true,
            } : false
        }),
        new CleanWebpackPlugin(),
        ...(process.env.NODE_ENV === 'production' ? [
                new MiniCssExtractPlugin({
                    filename: '[name].css' // 원본파일 이름 (이렇게 설정 안하면 해시값으로 내보냄), 근데 결국 js파일의 원본네임이니까 위의 main 키네임 가져오는 건 같음
                }),
            ] : []
        )
    ]
}

위와 같이 로더에도 process.env.NODE_ENV 환경변수 값에 따라 어떤 로더를 쓸지 분기처리한다.

NODE_ENV=production npm run build

그리고 노드 환경변수를 production으로 주고 빌드 명령어를 실행해보자.

그럼 위와 같이 main.css 파일이 생성된 것을 확인하실 수 있다.
그리고 dist/index.html을 보시면

이렇게 main.css도 link가 걸려있는 것을 확인할 수 있다.