내가 작성한 HTML, CSS, JavaScript 파일을 그대로 전송하는 것이 무슨 문제가 있을까요? 큰 문제가 없을 수 있습니다. 하지만 아래와 같은 상황들이 일어난다면, 어려움에 처할 수 있습니다.
이 중에서 용량이나 조작에 관한 문제는 프론트엔드 개발에서만 생기는 일은 아닙니다. 게임에서도 충분히 생길 수 있는 일입니다.
웹팩은 오픈 소스 자바스크립트 모듈 번들러로써 여러개로 나누어져 있는 파일들을 하나의 자바스크립트 코드로 압축하고 최적화하는 라이브러리입니다.
많은 자바스크립트 파일과 module과 sass 파일이든지 이미지 에셋들을 배포를 위한 정적인 에섯들로 만들어 줍니다.
리액트를 설치할 때 내부에서 이미 웹팩을 사용해서 Development Environment 개발 환경을 생성합니다. 그래서 리액트를 사용할 때 아무런 설정없이 다른 파일에 있는 함수를 import 하고 이미지를 사용할수 있고 CSS 그리고 소스 코드를 적용하면 바로 반영이 되는 등의 효과를 가져올수 있습니다.
여기에서는
Create-React-App을 이용하지 않고 웹팩을 처음부터 이용해서 개발환경을 만들어 보도록 하겠습니다.
의존성 그래프의 시작점을 웹팩에서는 엔트리(Entry)라고 한다.
웹팩은 엔트리를 통해서 필요한 모듈을 로딩하고 하나의 파일로 묶는다.
여러 개의 엔트리가 존재할 수 있다.
엔트리에 설정한 자바스크립트 파일을 시작으로 하나로 묶는다. 그 후 번들된 결과물을 처리할 위치를 output에 기록한다.
웹팩은 오직 JavaScript와 Json만 이해할 수 있다.
로더는 다른 Type의 파일(img, font, stylesheet 등)을 웹팩이 이해하고 처리 가능한 모듈로 변환시키는 작업을 한다.
로더가 파일 단위로 처리하는 반면 플러그인은 번들된 결과물을 처리한다.
로더가 변환하는 동안 플러그인은 bundle optimization, asset management and injection of environment과 같은 일을 진행할 수 있다.
프로그램을 구성하는 구성 요소의 일부
관련된 데이터와 함수들이 묶여서 모듈을 형성하고 파일 단위로 나뉘는 것이 일반적이다.
모듈화 프로그래밍은 기능별로 파일을 나눠가며 프로그래밍을 하는 것으로 유지보수가 쉽다는 장점이 있다.
최신 자바스크립트 문법을 지원하지 않는 브라우저들을 위해서 최신 자바스크립트 문법을 구형 브라우저에서도 돌수 있게 변환 시켜주는 라이브러리
// Babel Input: ES6(ES2015) arrow function
[1, 2, 3].map((n) => n + 1);
// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});
dist - src 에 들어 있는 코드들이 배포를 위해서 정적인 에셋들로 모이게 되는 공간입니다.
react를 사용할때는 npm run build 명령어를 사용해서 나오는 폴더와 같은 공간입니다.
결국은 dist에 있는 파일을 이용해서 화면에 UI나 기능들이 보이게 됩니다.
src - 애플리케이션을 위해 작성해야하는 코드는 이 source 폴더 안으로 들어갑니다.
npm init -y
npm i -D webpack webpack-cli
npm run build
이미 만들어진 라이브러리 import 해보기
npm install nanoid --save
npm run build
파일이나 디렉터리의 경로를 다룰 때 Node.js에서 있는 path 모듈을 사용할 수 있습니다.
path 모듈은 Node.js에 내장되어 있어 있기 때문에
별도의 라이브러리 설치 없이 바로 불러와서 사용할 수 있습니다.
resolve()를 이용해서 경로를 만들수 있습니다.
ex) path.resolve("Users", "john", "index.html")
=> 'Users/john/index.html'
const path = require('path');
module.exports = {
mode: 'delelopment',
// 시작점
entry: path.resolve(__dirname, 'src/index.js'),
// 웹팩 작업을 통해 생성된 결과물
output: {
path: path.resolve(__dirname, 'dist),
filename: 'main.js',
}
}
const path = require('path')
console.log(__dirname_)
// "/Users/hyena/Desktop/webpack-basic'
console.log(path.resolve(__dirname, 'src/index.js'))
// "/Users/hyena/Desktop/webpack-basic/src/index.js'
만약 여러개의 entry point를 가지는 경우
const path = require('path');
module.exports = {
mode: 'development';
//시작점
entry: {
main: path.resolve(__dirname, 'src/index.js'),
},
// 웹팩 작업을 통해 생성된 결과물
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
npm i -D css-loader style-loader sass sass-loader
npm run build
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
// Create `style` nodes from JS strings
'style-loader',
// Translates CSS into Common JS
"css-loader',
// Compiles Sass to css
"sass-loader",
],
},
],
},
};
npm i -D css-loader style-loader sass sass-loader
The css-loader interprets @import and url() like import/require() and will resolve them.
css-loader는 @import 및 url()을 import/require()와 같이 해석하고 해결합니다.
jsx처럼 자바스크립트를 이용한 마크업에 스타일링을 입힐 때는 import "style.css"와 같이 css 파일을 임포트 합니다.
임포트 하는 것은 css 파일을 하나의 모듈로 취급하여 js 파일에서 불러 사용한다는 것입니다.
여기서 css 파일을 모듈로 부르기 위해서 사용하는 로더가 css-loader이다.
css-loader를 이용해 웹팩 의존성 트리에 추가한 후에 css에 작성한 string 값들을 style-loader를 이용해서 돔에 로 넣어줍니다.
style-loader는 css-loader를 이용해서 import 구문을 이용해 js 파일에 불러들여진 스타일 파일이 html 파일 안에 style 태그로 존재할 수 있게 합니다.
웹팩은 로더와 플러그인의 확장 기능이 있습니다.
웹팩의 플러그인은 로더가 할 수 없는 다른 작업을 수행할 목적으로 제공됩니다.
로더는 모듈을 output으로 만들어가는 과정에서 사용합니다.
플러그인은 webpack으로 변환한 파일에 추가적인 기능을 더하고 싶을 때 사용합니다.
(최종적인 결과물을 변형시킵니다.)
플러그인은 웹팩의 기본적인 동작에 추가적인 기능을 제공하는 속성입니다.
npm i -D html-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
})
]
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({
- title: 'Output Management',
+ title: 'Caching',
}),
],
output: {
- filename: 'bundle.js',
+ filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
npm run build
소스 코드 변경 없이 빌드 => 아무런 파일도 생성되지 않습니다.
소스 코드 변경 후 빌드
console.log(nanoid());
console.log(getRandomAddress());
console.log(nanoid());
console.log(nanoid());
console.log(nanoid());
새로운 빌드 파일 생성
이전 해시 파일을 자동으로 지워주려면?
// 웹팩 작업을 통해 생성된 결과물
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name][contenthash].js',
clean: true
},
const path = require('path');
module.exports = {
//...
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
},
};
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true,
port: 3000,
open: true,
},
제공되는 모든 항목에 대해 gzip 압축을 활성화합니다.
서버가 시작된 후 브라우저를 열도록 dev-server에 지시합니다. 기본 브라우저를 열려면 true로 설정하세요.
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},
gzip 압축 없이 브라우저에서 데이터 보여주는 방법
여기서의 문제점은 ?
시스템은 작동하지만 그다지 효율적이지 않습니다. 100KB는 많은 텍스트이고 HTML은 중복됩니다.
모든 <html> ,<table> 및 <div>
태그에는 거의 동일한 닫는 태그가 있습니다.
문서 전체에 걸쳐 단어가 반복됩니다.
이러한 문제점을 해결하기 위해서 zip을 해주면 됩니다.
일반 이전 index.html 대신 .zip 파일을 브라우저(index.html.zip)로 보낼 수 있다면 대역폭과 다운로드 시간을 절약할 수 있습니다. 브라우저는 압축 파일을 다운로드하고 압축을 풀고 페이지가 빠르게 로드되어 사용자에게 보여줄 수 있습니다.
npm install -D babel-loader @babel/core @babel/preset-env
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
{
tset: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
+ module: {
+ rules: [
+ {
+ test: /\.png/,
+ type: 'asset/resource'
+ }
+ ]
+ },
};
src/index.js
import mainImage from './images/main.png';
img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
모든 .png 파일은 출력 디렉토리로 내보내지고 해당 경로는 번들에 주입됩니다.
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
type: 'asset/resourse'
}
# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer
사용방법
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}