기억의 실마리
2023. 11. 21. 00:31

Webpack

webpack은 Node.js 런타임 기반의 웹어플리케이션을 구성하는 자원을 모듈

단위로 번들링 해주는 도구이다. 프론트엔드 개발 트렌드로 자주 사용되는

라이브러리, 프레임워크(React, Vue 등)는 프로젝트 파일의 크기가 큰 경우

대다수이기 때문에 webpack을 통하여 파일들간의 의존성 관계를 정리하고

코드를 최적화하여 하나의 스크립트 파일로 만들어 주로 최적화된 서비스를

제공하기 위해 사용된다.

 

Webpack의 장점

  • 코드를 축소하여 최적화하고 사용하지 않는 코드를 제거하여 빌드하는 방식으로 성능을 최적화할 수 있다.
  • CSS가 아닌 SASS 혹은 stylus, Typescript 사용 시 컴파일 과정에서 필요 플러그인을 추가하고 번들러를 실행해준다.
  • 종속성 문제의 해결

 

Webpack의 단점

  • 일부 패키지들을 loader를 통해 매번 추가해주어야 하며 추가하고자 하는 자원의 타입에 따라 추가해 주어야 하는 loader가 다를 수 있기 때문에 러닝커브가 다소 있는 편이다.

 

Install

# 웹팩, 개발자 서버 인스톨
npm i -D webpack webpack-dev-server

# 필요한 플러그인 및 로더 설치 (css, sass, static copy plugin, env 등)
npm i -D html-webpack-plugin dotenv-webpack css-loader copy-webpack-plugin cross-env source-map-loader sass-loader clean-webpack-plugin

웹팩(개발자 서버 포함)과 필요한 로더와 플러그인을 설치한다.

 

webpack.config.js 분리

html / css / vanillaJS 구성으로 웹팩 적용하였으며 config 작성할 때 common, dev, prod로

나누어 빌드환경에 맞춰 별도로 관리해주었다.

 

webpack.common.js

공통으로 적용 될 config를 작성한다.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const DotEnv = require('dotenv-webpack');

module.exports = {
	entry: {
		main: path.resolve(__dirname, '../src/script.js'),
	},
	output: {
		path: path.resolve(__dirname, '../public'),
		filename: '[name].min.js'
	},
	// target: ['web', 'es5'], // es5 환경 작업 시 사용
	module: {
		rules: [
			{
				test: /\.js$/,
				loader: 'babel-loader',
				exclude: /node_modules/
			},
			{
				test: /\.js$/,
				enforce: 'pre',
				use: ['source-map-loader'],
			},
			{
				test: /\.s?css$/,
				use: [
					'style-loader',
					'css-loader',
					'sass-loader'
				]
			},
			{
				test: /\.(glb|gltf)$/,
				use: ['file-loader']
			},
		]
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: path.resolve(__dirname, '../src/index.html'),
			filename: 'index.html',
			minify: true,
		}),
		// origin 그대로 복사할 파일을 지정
		new CopyWebpackPlugin({
			// 빌드 시 public 폴더에 자동 생성(patterns 경로에 해당 파일이 없으면 에러발생)
			patterns: [
				{ from: path.resolve(__dirname, '../static') },
				{ from: path.resolve(__dirname, '../src/style.css') },
			]
		}),
		// dotenv-webpack패키지를 통한 환경변수(env) 사용
		new DotEnv({ path: '.env' }),
	]
};

 

 

webpack.dev.js

webpack.common.js에 추가적으로 개발자 서버에서만 적용될 config를 작성한다.

const path = require('path')
const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')

module.exports = merge(
    commonConfiguration,
    {
        mode: 'development',
        devServer: {
            liveReload: true,
            static: {
                directory: path.resolve(__dirname, '../public')
            },
            // hot: true,
            watchFiles: ['src/**/*'],
            port: 3000,
        },
    }
)

 

webpack.prod.js

webpack.common.js에 추가적으로 빌드 시 적용될 config를 작성한다.

const { merge } = require('webpack-merge')
const commonConfiguration = require('./webpack.common.js')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');

module.exports = merge(
    commonConfiguration,
    {
        mode: 'production',
        module: {
            rules: [
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    use: ['babel-loader']
                },
                {
                    test: /\.s?css$/,
                    use: [MiniCssExtractPlugin.loader, 'style-loader', 'css-loader', 'sass-loader']
                }
            ]
        },
        optimization: {
            minimizer: [
                new TerserPlugin({
                    terserOptions: {
                        compress: {
                            // 빌드 시 콘솔로그 drop
                            drop_console: true
                        }
                    }
                })
            ],
            splitChunks: { chunks: 'all' }
        },
        plugins: [
            new CleanWebpackPlugin(),
            new MiniCssExtractPlugin(),
            // build 과정에서 환경변수 사용 가능하도록 설정
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), // 환경변수 접근 필수 지정!
                'process.env.FIREBASE_API_KEY': JSON.stringify(process.env.FIREBASE_API_KEY),
                'process.env.FIREBASE_AUTH_DOMAIN': JSON.stringify(process.env.FIREBASE_AUTH_DOMAIN),
                'process.env.FIREBASE_PROJECT_ID': JSON.stringify(process.env.FIREBASE_PROJECT_ID),
                'process.env.FIREBASE_SENDER_ID': JSON.stringify(process.env.FIREBASE_SENDER_ID),
                'process.env.FIREBASE_APP_ID': JSON.stringify(process.env.FIREBASE_APP_ID),
                'process.env.FIREBASE_MEASUREMENT_ID': JSON.stringify(process.env.FIREBASE_MEASUREMENT_ID),
            })
        ]
    }
)

 

package.json

scripts를 수정하여 webpack을 적용한다.

{
  
  "scripts": {
    "start": "webpack serve --open --config ./bundler/webpack.dev.js",
    "build": "webpack --config ./bundler/webpack.prod.js"
  },
 
}

 

 

마치며...

주로 react를 create-react-app을 통해 보일러플레이트를 설치하여 개발을 진행하였고 webpack은 create-react-app을 통해 함께 설치되며 최적화에 도움을 준다는 기본적인 개념만 알고있었다.

 

이렇게 웹팩을 직접 프로젝트에 도입하며 경험해보니 그 동안 create-react-app을 통해 react 보일러플레이트를 설치하고 이미 구성되어있는 편리한 환경속에서 webpack을 사용하고 있었다는 것을 깨달았다.

 

생각했던 것 보다 높은 자유도 때문인지 러닝커브를 느꼈던 것 같고 특히나 환경변수를 적용하는 것이 가장 어려웠다...

 

항상 그렇듯 알고나면 별거 아니지만 그 과정은 험난했고, 이 후에 문제를 극복하며 따라오는 만족감은 이로 말할 수 없는 것 같다. 아직도 갈 길이 멀었음을 깨달았고 다시 한 번 더 열심히 정진해야겠다는 다짐을 굳히는 시간들이었다.