2021-11-08 13:55 작성

Code Hacking 1 - Webpack configuration

Table of contents

Code Hacking 코너는 이미 쓰여진 코드들 중 궁금한 부분들을 공부하기 위해 만든 스스로를 위한 코너입니다.

1. 도입

Three JS를 공부하며 DesignCourse의 도움을 많이 받았다. Three JS를 통해 간단하지만 멋진 사이트를 만들 수 있게 실제적 예시를 많이 제공해주었고 Three JS의 개념 설명을 차근차근 잘해주는 좋은 유튜브 학습과정이었다. 뿐만 아니라 DesignCourse에서는 빠른 코딩을 위해 WebpackStarter를 제공해주었고 그 덕분에 수월하게 시작할 수 있었다. 그러다 문득 Webpack 설정이 어떻게 되어 있길래 내가 이렇게 편하게 사용할 수 있는 건지 궁금해졌다. 사용할 당시에는 Webpack이 번들링에 필요하고 그저 퀵스타트 기본 설정 밖에 못하는 수준이었기에 다음에 하자고 마음을 먹었지만 이제서야 분석을 해봐야겠다고 결정을 내렸다.

2. 분석 대상

이번 분석 대상은 Webpack configuration과 관련된 부분에 한정한다. 따라서 webpack.common.js, webpack.dev.js, webpack.prod.js만 기술적 분석을 할 것이다.

3. 분석

A. package.json

"scripts": {
    // webpack cli를 이용해 production build 시 사용할 구체적 configuration을 지정 및 실행
    "build": "webpack --config ./bundler/webpack.prod.js",
    // webpack cli를 이용해 development build 시 사용할 구체적 configuration을 지정, serve는 devServer 구동을 위한 명령어
    "dev": "webpack serve --config ./bundler/webpack.dev.js"
},

B. webpack.common.js

webpack 설정에 있어 기본 틀을 구성하는 webpack config 파일로 webpack.dev.jswebpack.prod.jswebpack.common.js를 기본으로하여 구성한다.

// 이미 존재하는 파일이나 전체 폴더를 build 할 폴더 내에 복사하는 plugin
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 매 build 마다 달라지는 hash가 포함된 webpack 번들을 자동으로 추가해주는 plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// js 파일 당 css 파일 단위로 나누어주는 plugin으로 On-Demand-Loading을 지원하는 plugin
const MiniCSSExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')

module.exports = {
    // budling을 시작할 포인트
    entry: path.resolve(__dirname, '../src/script.js'),
    // bundling을 통해 나올 결과 산출물로 dist 폴더 내에 bundle.[contenthash].js 형태로 저장
    output:
    {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, '../dist')
    },
    // debugging에 필요하며 브라우저에서 source-map을 reconstructuring이 가능하게 해 original code에 접근이 가능하게 함
    devtool: 'source-map',
    plugins:
    [
        // static 폴더 안에 있는 image 등의 복사될 파일을 지정 dist 폴더 바로 아래 파일 혹은 폴더가 생성 됨 (build 시 참고)
        new CopyWebpackPlugin({
            patterns: [
                { from: path.resolve(__dirname, '../static') }
            ]
        }),
        // src 폴더 안에 존재하는 index.html 파일을 타겟으로 하여 hash가 포함된 js bundle을 자동으로 추가
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, '../src/index.html'),
            minify: true
        }),
        // css를 bundle js 단위로 나눔
        new MiniCSSExtractPlugin()
    ],
    module:
    {
        rules:
        [
            // HTML
            {
                test: /\.(html)$/,
                use: ['html-loader']
            },

            // JS, 최신 es 문법을 babel을 통해 전환
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use:
                [
                    'babel-loader'
                ]
            },

            // CSS
            {
                test: /\.css$/,
                use:
                [
                    MiniCSSExtractPlugin.loader,
                    'css-loader'
                ]
            },

            // Images
            {
                test: /\.(jpg|png|gif|svg)$/,
                use:
                [
                    {
                        loader: 'file-loader',
                        options:
                        {
                            outputPath: 'assets/images/'
                        }
                    }
                ]
            },

            // Fonts
            {
                test: /\.(ttf|eot|woff|woff2)$/,
                use:
                [
                    {
                        loader: 'file-loader',
                        options:
                        {
                            outputPath: 'assets/fonts/'
                        }
                    }
                ]
            }
        ]
    }
}

C. webpack.dev.js

Development webpack configuration.

// webpack.common.js configuration을 merge 할 수 있게 함
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
// local ip 주소
const ip = require('internal-ip')
// port 지정
const portFinderSync = require('portfinder-sync')

// console 내 실행되는 ip와 port를 효과적으로 보여주기 위한 색 적용
const infoColor = (_message) =>
{
    return `\u001b[1m\u001b[34m${_message}\u001b[39m\u001b[22m`
}

module.exports = merge(
    commonConfiguration,
    {
        mode: 'development',
        devServer:
        {
            // host ip를 모를 경우 0.0.0.0으로 지정하며 DHCP 서버에서 실제 ip주소를 가져옴
            host: '0.0.0.0',
            port: portFinderSync.getPort(8080),
            // 업데이트 및 참고할 content 위치
            contentBase: './dist',
            // contentBase에 위치한 content 정보가 바뀌었을 경우 reload
            watchContentBase: true,
            // cli를 실행시켰을 때, 브라우저에서 자동 연결이 되도록 설정
            open: true,
            // https 사용 여부
            https: false,
            // local ip 사용 여부
            useLocalIp: true,
            // host check 무시 여부, DNS rebinding attack에 취약해 추천하지 않음
            disableHostCheck: true,
            // error 발생 시 화면 전체 출력 여부
            overlay: true,
            // console 내 bundle 과정 생략 여부
            noInfo: true,
            after: function(app, server, compiler)
            {
                const port = server.options.port
                const https = server.options.https ? 's' : ''
                const localIp = ip.v4.sync()
                const domain1 = `http${https}://${localIp}:${port}`
                const domain2 = `http${https}://localhost:${port}`
                
                // 현재 연결된 domain 표시
                console.log(`Project running at:\n  - ${infoColor(domain1)}\n  - ${infoColor(domain2)}`)
            }
        }
    }
)

D. webpack.prod.js

Production configuration.

const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
// webpack output.path 폴더에 존재하는 모든 파일을 제거하고 사용하지 않는 assets를 제거한 뒤에 rebuilding하는 plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = merge(
    commonConfiguration,
    {
        mode: 'production',
        plugins:
        [
            new CleanWebpackPlugin()
        ]
    }
)

4. 참고

ip v4 0.0.0.0

라이센스

크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.