webpack 기초개념 & 기본 설정

전창현·2020년 8월 23일
1

Reference : webpack.docs

Concepts

webpack은

모든 file을 module로 간주하며

project에 필요한 모든 모듈을 dependency graph에 따라 번들링하는 도구이다.

번들링된 결과물을 bundle이라 한다.

  • Entry
  • Output
  • Loaders
  • Plugins
  • Mode
  • Browser Compatibility

Entry

  • dependency graph의 시작점
  • default : ./src/index.js
module.exports = {
	entry: './path/file.js';
};

Output

  • dependency graph에 따라 번들링된 결과물(bundle)을 저장할 path와 파일명
  • default : ./dist/main.js
const path = require('path');

module.exports = {
  entry: './path/file.js';
    output: {
	  path: path.resolve(__dirname, 'dist');
	  filename: 'myBundle.js';
    } 
};

Loaders

box 밖에서, webpack은 Javascript 언어로 작성된 파일(module)만 읽어들일 수 있다.

따라서 Loaders를 사용해 다른 형식의 파일(module)을 valid한 modules로 바꾸고 dependency graph에 추가한다.

loaders는 2가지의 property를 갖는다.

  1. test
    • 어떤 파일이 transform될지
  2. use
    • 어떤 로더가 transform에 사용될지 명시
npm install --save-dev css-loader ts-loader
const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
	  { test: /\.css$/, use: 'css-loader' },
	  { test: /\.ts$/, use: 'ts-loader'}
    ]
  }
};

import / require() 선언에 
.txt 파일이 포함될 떄
raw-loader를 사용해서 transform하고, bundle에 추가

정규식에 quote를 추가하지 말 것!

동일한 regex에 대해 다수의 loader를 추가할 경우

right에서 left로 실행된다. (array에서 pop하기 떄문)

Using loaders

  1. configuration : webpack.config.js에 명시
  2. inline : import문
  3. CLI : shell command 사용

configuration

npm install --save-dev css-loader style-loader sass-loader
module.exports = {
  module: {
    rules: [
	  {
	    test: /\.css$/,
		use: [
		  { loader: 'style-loader'},
  		  { 
          	loader: 'css-loader',
            options: { module: true }
      	  },
          { loader: 'sass-loader'}
 	    ]
      }
    ]
  };
};

Plugins

loader가 할 수 없는 모든 역할을 수행

plugins는 bundle 최적화, assets management, 환경변수 injection과 같은 역할을 수행한다.

대부분의 plugins는 options를 통해 커스터마이징이 가능하며

new operator를 통해 여러 인스턴스를 생성할 수 있다.

  1. require
  2. new Plugin(options)를 통한 인스턴스 생성
  3. module.exports.plugins: []의 item으로 추가
npm i --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // built in plugins

module.exports = {
	entry: 'index.js',
	output: {
		path: path.resolve(__dirname, './dist'),
		filename: 'index_bundle.js'
	},
	module: {
		rules: [
			{ test: /\.txt$/, use: 'raw-loader' }
		]
	},
	plugins: [
		new HtmlWebpackPlugin({template: './src/index.html'})
	],
};

// dist/index.html이 생성된다.
// entry point가 여러 개 일 경우, script로 포함된다.

Mode

  • development : default
  • production : production에서 파일들을 minify한다.
  • none
module.exports = {
	mode: 'production'
};

reference : https://www.valentinog.com/blog/webpack/

1. webpack 설치

npm init -y

npm i webpack webpack-cli webpack-dev-server --save-dev

2. package.json scripts 추가

// in package.json

"scripts": {

	"dev": "webpack --mode devleopement"

}

development mode

  • DefinePlugin의 노드 환경변수를 Development로 변경
  • namedChunksPlugin
  • namedModulesPlugin

production mode

  • DefinePlugin의 노드 환경변수를 production으로 변경
  • production 모드에서 사용하는 plugins를 enable로 세팅

3. webpack.config.js

1) entry point

2) output

3) loaders

4) plugins

5) code splitting

  • entry point & output
// webpack.config.js

const path = require('path');

module.exports = {
	entry: {
		index: path.resolve(__dirname, 'source', 'index.js');
	},
	output: {
		path: path.resolve(__dirname, 'build')
	}
}

4. working with HTML

HtmlWebpackPlugin : bundle을 로딩할 HTML파일을 생성해주는 플러그인

npm i html-webpack-plugin --save-dev

// webpack.cofig.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {

output: {
	path: path.resolve(__dirname, './dist'),
	filename: 'bundle.js'
},

plugins: [
	new HtmlWebpackPlugin({
		title: 'myApp',
		template: './src/template.html', // DOM element를 추가하기 위해 설정
	}),
	new HtmlWebpackPlugin({
		title: 'chang hyun', // title 태그
		filename: 'index2.html', // output file 이름
		inject: 'head', // script 위치 결정
		scriptLoading: 'defer',
		// favicon: 'path',
		meta: { // meta 태그
			viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no'
		},
		base: 'https://naver.com/path/', // nase 태그,
		minify: true, // minify 유무
		hash: true, // scripts , css 파일 hash 컴파일링 옵션 (브라우저 캐싱 방지로 사용)
		cache: true, // 캐싱을 통해 변경된 파일만 emit
		showErrors: true, // 에러 발생 시 html 파일에 detail이 표기됨
})]

}

// 아래 깃헙 링크를 참고하면 여러 세팅이 가능
https://github.com/jantimon/html-webpack-plugin#options

5. webpack-dev-server

// package.json

"scripts": {
	"start": "webpack-dev-server --mode development --open"
}

local server를 launch

6. loader

// webpack.config.js

module.exports = {

	module: {
		rules: [
			{
				test: /\.filename$/,
				use: ["loader-b", "loader-a"]
			}
		]
	},

};
  • test : 모듈화할 file type
  • use : file 모듈화에 필요한 loader

style loaders

css-lodaer : CSS file type을 모듈화해 import가 가능해짐

style-loader : js로 변환된 stylesheet을 DOM에 입힘

npm i css-loader style-loader --save-dev

// webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
	
	module: { // 다른 type을 loader를 통해 모듈화
		rules: [
			{
				test: /\.s|css$/,
				use: ["style-loader", "css-loader", "sass-loader"] // css type을 바꾼 후, style을 적용해야 되므로 순서에 유의할 것 (LIFO)
			}
		]
	},
	
	plugins: [
		new HtmlWebpackPlugin({
			template: path.resolve(__dirname, 'src', 'index.html')
		})
	]

};
// index.js

import './css/style.css';
import './css/style.scss';

es6 import는 synchronous하므로 specification이 동일한 selector가 있을 경우

style.sass의 스타일링에 적용됨

babel

npm i @babel/core babel-loader @babel/preset-env --save-dev
// babel.config.js

{
	"presets": [
		"@babel/preset-env"
	]		
}

// webpack.config.js

module.exports = {

	module: {
		rules: [
			{
				test: /\.scss$/,
				use: ["style-loader", "css-loader", "sass-loader"]
			},
			{
				test: /\.js$/,
				exclude: /node_modules/,
				use: ["babel-loader"]
			}
		]
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: path.resolve(__dirname, "src", "index.html")
		})
	]

};

Production mode

webpack은 두 종류의 mode로 나뉜다.

  • development

- bundle을 browser로 load하며

- minification을 진행하지 않기 때문에 빠르게 reloading이 가능.

  • production

- TerserWebpackPlugin을 통해 번들 사이즈를 줄이고

- ModuleConcatenationPlugin을 통해 호이스팅을 scope화한다.

- process.env.NODE_ENV 환경변수를 'production'으로 설정한다.

- 환경변수를 통해 조건적으로 production모드에 적용할 수 있음.

// in package.json

"scripts" : {

"dev": "webpack --mode development",
"start": "webpack-dev-server --mode development --open",
"build": "webpack --mode production"

}

Code splitting

최적화 기법 중 하나로

  • big bundle을 잘개 쪼개고
  • dependencies duplication을 방지한다.

주요 기법

  • multiple entry points

    - 작은 프로젝트에서만 효과적

  • optimization.splitChunks

  • dynamic imports

1. optimization.splitChunks

npm i moment

// index.js

import moment from 'moment';

npm run build

/*
 index.js에서 대용량 라이브러리를 import해 그대로 빌드할 경우 
 라이브러리를 포함한 모든 코드가 bundle로 올라가게 되는데,
 
 webpack.config의 optimization.splitChunks를 통해 
 moment.js를 main bundle 밖의 다른 번들로 분리하면 main 번들의 로딩 타임을 줄일 수 있게 된다.

*/
// webpack.config.js

module.exports = {

	optimization: {
		splitChunks: { chunks: "all" }
	},

};

2. dynamic imports

Code splitting might be used

  • at the module level
  • at the route level

module level에서의 dynamic import를 통한 code splitting 기법을 살펴보자면

const getUserModule = () => import('./common/usersAPI')
// getUserModule을 실행할 경우, import하여 모듈을 리턴한다.

const btn = document.getElementById('btn');
btn.addEventListener('click', () => {

	getUserModule().then(({ getUsers }) => {
		getUsers().then(json => console.log(json))
	});

})

CRA를 사용하지 않고 react를 사용하기 위한 기본 세팅

  • react
  • react-dom
  • webpack
  • webpack-cli
  • webpck-dev-server
  • html-webpack-plugin
  • @babel/core
  • @babel/preset-env
  • @babel/preset-react
  • babel-loader
  • style-loader
  • css-loader
  • sass-loader

1. package.json

"scripts": {
    "dev": "webpack --mode development",
    "start": "webpack-dev-server --mode development --open --hot",
    "build": "webpack --mode production"
  },

2. webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: path.join(__dirname, 'src', 'index.js'),
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    },
    module: {
        [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            },
            {
                test: /\.s|css$/,
                exclude: /node_modules/,
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ]
}

3. babel.config.json

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

1개의 댓글

comment-user-thumbnail
2021년 8월 19일

안녕하세요 웹팩 정리하신 글 잘보았습니다! 그런데 혹시 동일한 regex에 대해 다수의 loader를 추가할 경우right에서 left로 실행된다. (array에서 pop하기 떄문) 여기서 array 에서 pop 하기 때문이라는 이유는 어디서 보셨는지 알수있을까요!!?

답글 달기