웹팩을 기초부터 하나씩 (2) : webpack.config.js
webpack.config.js는 다음과 같이 구성되어 있습니다.
하나씩 톺아봅시다.
const path = require("path");
const config = {
mode: "none",
entry: path.resolve(__dirname, "assets", "js", "main.js"),
module: {
rules: [
{rule1},
{rule2},
{
test: /\.(scss)$/,
use: ExtractCSS.extract([
{
loader: "css-loader",
},
{
loader: "postcss-loader",
options: {
plugin() {
return [autoprefixer({ browsers: "cover 99.5%" })];
},
},
},
{
loader: "sass-loader",
},
]),
}
],
},
output: {
path: path.join(__dirname, "static"),
filename: "[name].[format]",
},
};
module.exports = config;
mode
웹패 v4 부터 생긴 기능입니다.
- none : 모드 설정 안함
- development : 개발 모드
- production : 배포 모드
node_env 환경 변수를 체크해서 모드를 바꿔주면 되겠죠?
아니면 스크립트 단에서 다음과 같이 명령어를 날려줘도 됩니다.
webpack --mode=development
배포 모드일 때 조금 더 빌드가 빡세게(?) 된다고는 하는데 그렇다고 none으로 하면 번들링이 안되는 것도 아닙니다. 배포 단계에서만 신경 쓰면 됩니다.
entry
entry는 웹팩이 번들링을 시도하는 진입점입니다. 진입점을 설정하는 것이 중요한 이유는 entry를 기점으로 import 된 모듈들을 찾아서 번들링하기 때문입니다.
CRA를 eject해서 entry가 어디인지 살펴보도록하겠습니다.
entry: [
// isEnvDevelopment는 webpackEnv를 dev냐 prod냐를 체크합니다.
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
].filter(Boolean),
웹팩 환경이 개발이냐 프로덕션이냐를 따져서 entry를 설정하는 군요
[].filter(Boolean)은 다음과 같이 작동합니다.
[1, 3, "", false].filter(Boolean) // output [1, 3]
참고로 entry는 반드시 한 개일 필요는 없습니다. 멀티 페이지 어플리케이션에서는 여러 개의 엔트리 포인트를 줄 수 있습니다.
entry: {
login: './src/LoginView.js',
main: './src/MainView.js'
}
output
번들링한 결과물을 어디 경로에 놓을 것인지를 결정합니다.
파일이름과 path를 정해놓으면 그곳에 정의해둡니다.
output: {
path: path.join(__dirname, "static"),
filename: "[name].js",
},
filename에는 여러 옵션을 넣어줄 수 있습니다.
// 결과 파일 이름에 entry 속성을 포함하는 옵션
filename: "[name].js", // main.js
// 웹팩 내부에서 부여하는 id (0, 1, 2...)
filename: "[id].js", // 0.js
// 웹팩 내부에서 부여하는 해시값
filename: "[hash].js", // 4d4b66c6905bd02ef198.js
// 모듈 내용을 기준으로 생생된 해시 값을 붙이는 옵션 (그냥 해쉬죠 뭐...)
filename: "[chunkhash].js", // 609f69712b6a1a422a1f.js
// 짬뽕하기
filename: "[name].[id].[hash].js",
CRA의 output은 어떨까요? 아, 꽤나 복잡하네요
output: {
path: isEnvProduction ? paths.appBuild : undefined,
pathinfo: isEnvDevelopment,
filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js',
futureEmitAssets: true,
chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js',
publicPath: paths.publicUrlOrPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
globalObject: 'this',
},
loader
앞서 작성한 엔트리 포인트에 css를 살포시 얹어보겠습니다. css 내용은 중요하지 않습니다.
import "./style.css";
function component() {
const newDiv = document.createElement("div");
newDiv.innerHTML = "hello guys";
return newDiv;
}
document.body.appendChild(component());
이제 빌드를 시도해보면,
ERROR in ./src/style.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
그러니까 css를 처리하는 적절한 로더가 없다는 것입니다.
css를 처리하는 로더는 "css-loader"입니다. 웹팩 홈페이지에서 찾아볼 수 있습니다.
(https://webpack.js.org/loaders/css-loader/)
말하는 대로 고분고분 설치해줍시다.
npm i css-loader -D
다음과 같이 webpack.config.js를 구성해주면됩니다.
test는 적용할 파일의 유형, use는 적용할 로더입니다.
test에는 정규식이 사용됩니다. /\.css$/ 의 의미를 뜯어보면 //는 정규식 이란 의미이며 \.는 .이 특별한 의미가 아닌 일반 문자인 .을 의미한다는 것이며 css$는 css로 끝나는 것을 의미합니다.
const path = require("path");
module.exports = {
mode: "none",
entry: "./src/index.js",
module: {
rules: [
{
test: /\.css$/,
use: ["css-loader"],
},
],
},
output: {
filename: "[name].js",
path: path.resolve("dist"),
},
};
로더에 대한 내용은 https://webpack.js.org/loaders/ 여기서 참고해보시면 될 것 같습니다.
아, 중요한 사실을 하나 빼먹었는데, css의 경우 js와 코드를 분리해줘야 합니다.
mini-css-extract-plugin를 사용하시면 됩니다.
https://github.com/webpack-contrib/mini-css-extract-plugin
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "none",
plugins: [new MiniCssExtractPlugin()],
entry: "./index.js",
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{ loader: "css-loader" },
],
},
],
},
output: {
filename: "[name].js",
path: path.resolve("dist"),
},
};
일반적으로 scss는 sass-loade, postcss-loader, css-loader 순으로 로더를 적용합니다.
(로더는 일반 코드와는 반대로 아래부터 적용됩니다!)
test: /\.scss$/,
use: [
{ loader: 'css-loader' },
{
loader: 'postcss-loader'
},
{ loader: 'sass-loader' }
]
babel을 적용한 JS는 babel-loader면 뚝딱입니다.
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
},
],
},
이 것 외에도 사용되는 로더를 나열해보자면 다음과 같은 것이 있다고 합니다. 필요한 상황에 맞게 loader를 찾아보는 것이 중요하겠습니다.
plugin
로더가 코드를 변환한다면 플러그인은 번들링된 결과물을 변환한다고 보시면 되겠습니다.
간단히 웹팩에 내장된 ProgressPlugin 플러그인을 사용해보겠습니다. 웹팩의 빌드 진행을 프로그레시브하게 알려주는 플러그인입니다.
const path = require("path");
const webpack = require("webpack");
module.exports = {
mode: "none",
// 플러그인을 살포시 추가!
plugins: [
new webpack.ProgressPlugin((percentage, message, ...args) => {
console.info(percentage, message, ...args);
}),
],
entry: "./src/index.js",
module: {
rules: [
{
test: /\.css$/,
use: ["css-loader"],
},
],
},
output: {
filename: "[name].js",
path: path.resolve("dist"),
},
};
출력 결과의 일부를 가져와보면 다음과 같습니다.
0 compiling
0.1 building 0/0 modules 0 active
0.1 building 0/1 modules 1 active C:\Users\BOSSMONSTER\Documents\webpacktest\src\index.js
0.10120000000000001 building 1/1 modules 0 active
0.10120000000000001 building 1/2 modules 1 active C:\Users\BOSSMONSTER\Documents\webpacktest\node_modules\css-loader\dist\cjs.js!C:\Users\BOSSMONSTER\Documents\webpacktest\src\
각종 플러그인들은 웹팩 공식 문서를 참고하시거나, github을 뒤져보시면 될 것 같습니다.
https://webpack.js.org/plugins/
사실 이 특성 외에도 resolve, DevServer, devtool이 존재합니다만, 기본적인 웹팩 사용은 이 정도로 이루어져 있습니다.
기본적으로 다음과 같은 loader가 있다고 webpack 홈페이지에 써있습니다.
Files
- raw-loader Loads raw content of a file (utf-8)
- val-loader Executes code as module and consider exports as JS code
- url-loader Works like the file loader, but can return a data URL if the file is smaller than a limit
- file-loader Emits the file into the output folder and returns the (relative) URL
- ref-loader Create dependencies between any files manually
JSON
- json5-loader Loads and transpiles a JSON 5 file
- cson-loader Loads and transpiles a CSON file
Transpiling
- babel-loader Loads ES2015+ code and transpiles to ES5 using Babel
- buble-loader Loads ES2015+ code and transpiles to ES5 using Bublé
- traceur-loader Loads ES2015+ code and transpiles to ES5 using Traceur
- ts-loader Loads TypeScript 2.0+ like JavaScript
- coffee-loader Loads CoffeeScript like JavaScript
- fengari-loader Loads Lua code using fengari
- elm-webpack-loader Loads Elm like JavaScript
Templating
- html-loader Exports HTML as string, require references to static resources
- pug-loader Loads Pug and Jade templates and returns a function
- markdown-loader Compiles Markdown to HTML
- react-markdown-loader Compiles Markdown to a React Component using the markdown-parse parser
- posthtml-loader Loads and transforms a HTML file using PostHTML
- handlebars-loader Compiles Handlebars to HTML
- markup-inline-loader Inline SVG/MathML files to HTML. It’s useful when applying icon font or applying CSS animation to SVG.
- twig-loader Compiles Twig templates and returns a function
- remark-loader Load markdown through remark with built-in image resolution
Styling
- style-loader Add exports of a module as style to DOM
- css-loader Loads CSS file with resolved imports and returns CSS code
- less-loader Loads and compiles a LESS file
- sass-loader Loads and compiles a SASS/SCSS file
- postcss-loader Loads and transforms a CSS/SSS file using PostCSS
- stylus-loader Loads and compiles a Stylus file
Linting && Testing
- mocha-loader Tests with mocha (Browser/NodeJS)
- eslint-loader PreLoader for linting code using ESLint
Frameworks
- vue-loader Loads and compiles Vue Components
- polymer-loader Process HTML & CSS with preprocessor of choice and require() Web Components like first-class modules
- angular2-template-loader Loads and compiles Angular Components
Awesome
For more third-party loaders, see the list from awesome-webpack.