항상 React와 Create React App을 사용해 잘 세팅된 개발 환경을 사용하고 굳이 웹팩을 커스터마이징해야하는 상황을 안 만들었다 보니 웹팩에 대해 잘 알지 못했다.
하지만 CRA가 너무 거대하다는 평이 있기도 하고 언젠가는 웹팩을 사용하여 boilerplate를 직접 만들어야 하는 상황이 올 것이기 때문에 프론트 개발자에게 웹팩을 다루는 지식은 필수일 것이다. 웹팩의 개념을 공부해보고 vanilla-js-boilerplate를 한번 만들어보자.
웹팩은 모듈 번들러
이다. 번들(Bundle)은 묶어 준다는 뜻인데 javascript, css, assets 파일들을 묶어 하나로 만들어 주는 것이다. 하나로 묶어주는 이유는 기본적으로 http 요청을 효율적으로 하기 위해서이다.
웹페이지가 로드되기 위해서는 html, js, css 파일 외에도, 웹폰트, favicon, 이미지, json 데이터 등 수 많은 파일들을 받아와야 한다. 옛날 http/1.1에서는 커넥션 하나만을 열어 하나의 요청이 끝나야 다음 요청을 보낼 수 있었기 때문에 요청이 많을수록 비효율적었다. 그리고 http/2가 보편화된 지금도 기본적으로 서버와 여러번 통신하게 되는것은 비효율적이다.(브라우저마다 HTTP 요청 횟수에 제한을 두고 있기도 하다.)
그리고 자바스크립트에서 모듈의 개념이 생겨서 서로 의존하는 코드를 작성할 수 있고 이 때문에 많은 js파일이 만들어지게 되었다. 이 파일들을 마지막에 하나의 js 파일로 합쳐주는것이 웹팩과 같은 모듈 번들러의 역할이라고 할 수 있다.
물론 수 많은 자바스크립트 파일이 하나의 파일로 묶인다면 초기 로딩 속도가 아주 느려질 수 있다. 모듈 번들러들은 이런 문제를 해결하기 위해 청크, 캐시, 코드 스플릿 개념(둘 이상의 js로 나누는 것)들을 도입하면서 이러한 문제들을 해결한다.
그럼 이제 webpack 공식문서(웹팩은 벌써 v5가 Release 되었다.)를 보면서 웹팩을 세팅해보고 기본적인 html,js,css를 번들링하는 boilerplate를 한번 만들어보자.
1) 웹팩 설치하기
mkdir vanilla-js-boilerplate
cd vanilla-js-boilerplate
npm init -y
npm i -D webpack webpack-cli
먼저 디렉토리를 만들고, npm을 init하고, webpack과 webpack-cli(터미널에서 webpack 명령어를 실행하기 위해)를 개발 디펜던시로 설치한다.
2) 파일 생성하기
실제로 빌드해보기 위해 간단한 html, js, css파일을 만들어 src폴더에 넣어 주자.
├── src
├── index.js
├── index.html
├── style.css
├── package.json
src/index.js
function component() {
const element = document.createElement("div");
element.innerHTML = "Hello World";
return element;
}
document.body.appendChild(component());
src/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Vanilla Js Boilerplate</title>
</head>
<body>
</body>
</html>
src/style.css
body {
background-color: darkgray;
}
3)webpack.config.js
webpack.config.js 파일은 웹팩 옵션들을 설정할 수 있는 파일이다.
webpack 명령어가 실행되면 디렉토리내에 webpack.config.js를 알아서 찾아서 그 안에 내용을 가지고 빌드 된다.(webpack v4부터는 config 파일을 굳이 생성 안해도 찾아서 없다면 디폴트 설정값으로 알아서 빌드한다.)
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "build"),
clean: true,
},
};
entry
는 웹팩이 어디를 출발지점으로 해서 번들할지 알려줄 때 사용한다. 웹팩이 번들링할 때는 특정 지점에서부터 파일간의 디펜던시를 찾아가며 번들링하는데 그걸 명시해줄 때 entry를 사용한다.
output
은 당연히 결과물을 어디로 내보낼지 지정하는 속성이다. 메인 결과물인 bundle.js 파일은 ./build/bundle.js에, 그 외 파일은 ./build 폴더에 내보내진다. clean을 true로 설정하면 결과물이 내보내지는 디렉토리안에 사용하지 않는 파일을 알아서 정리해준다. 이외에도 수많은 커스텀 옵션을 설정할 수 있다.
4)빌드하기
webpack
이제 webpack 명령어를 실행해보자. public이라는 폴더가 생기면서 안에 있는 index.js파일이 bundle.js로 번들된 것을 볼 수 있다!
만약 다른 이름의 config 파일(webpack.config.prod.js 등)을 가지고 번들링하고 싶다면 webpack 명령어를 실행할 때 아래와 같이 타켓팅해주면된다.
webpack --config webpack.config.prod.js
또한 빌드에는 세가지 옵션을 줄 수 있다.
1. production : 최적화되어 빌드 되어지는 것이 특징
2. development : 빠르게 빌드하는 것이 특징
3. none : 아무 기능 없는 것이 특징
config 파일에 명시할 수도 있지만 webpack 명령어를 실행할 때 플래그를 주어 구분하는 방식이 더 효율적이다. package.json에 scripts를 만들어 이용하자.
"scripts": {
"build": "webpack --mode=production",
"build:dev": "webpack --mode=development"
},
웹팩은 설정해 주지 않으면 기본적으로 JavaScript와 JSON 파일만 번들링한다. 그래서 다른 형식의 파일들을 번들링하기 위해서 로더(loader)를 사용한다. 사용하고 싶은 로더 패키지를 설치하고 webpack.config.js파일에 해당 로더를 설정해줄 수 있다. 다음과 같은 규칙으로 설정해주면 된다.
module : {
rules: {
test: '가지고올 파일들의 정규식',
use: [
{
loader: '사용할 로더의 이름',
options: { 사용할 로더의 옵션 }
}
]
}
}
test
안에 사용한 정규식을 가지고 entry
부터 번들해 나가면서 해당 파일을 찾아 로더에 적용한다.
index.js에 style.css를 불러오기 위해서 css-loader와 style-loader를 설치하고 옵션에 설정해주자.
npm i -D css-loader style-loader
src/index.js
import './index.css' // css파일 불러오기
function component() {
const element = document.createElement("div");
element.innerHTML = "Hello World";
return element;
}
document.body.appendChild(component());
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
이렇게 변경하고 npm run build
를 다시 실행해보자. css코드를 불러온 bundle.js를 확인할 수 있다. 여기서 중요한 점은 use 배열에 들어간 로더 목록은 끝에서 처음으로 읽어가기 때문에 의존하는 로더가 있는 경우는 순서를 잘 지켜줘야 한다.
이외에도 다양한 loader를 사용할 수 있다. 대표적으로는 babel-loader가 있는데, ES6+ 문법으로 작성된 js파일을 ES5문법으로 트랜스파일링 해주기 위해 바벨을 사용할때 babel-loader를 붙여서 번들링시 같이 작동하게 할 수 있다.
플러그인은 웹팩이 단순한 번들러 이상의 역할을 하게 해준다. 파일을 복사 한다던지, 빌드 정보를 추가 해준다던지, 별도 css 파일을 뽑아준다던지 등의 기능을 플러그인들을 통해서 가능하게한다. 웹팩이 인기 있는 이유는 이러한 플러그인들 때문이기도 할 것이다.
우리는 html-webpack-plugin을 추가해보자. 이 플러그인은 모든 번들을 포함하는 HTML 문서를 자동으로 생성해주는 기능을 가지고 있다. 또한 템플릿을 설정 할 수 있어 뼈대가 될 html을 만들어 둘 수도 있다. 아까 만들어둔 index.html 파일을 템플릿으로 사용해보자.
npm i -D html-webpack-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: ['css-loader'],
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
이렇게 설정해준후 npm run build
해보면 bundle.js를 스크립트 파일로 포함하는 index.html파일이 build 폴더에 생성된 것을 볼 수 있다.
또한 웹팩은 로컬서버를 열어 코드를 수정했을 때 다시 빌드하고 핫리로드를 적용해 바로바로 빌드 결과를 확인할 수 있는 기능도 제공한다. webpack-dev-server를 설치해보자.
npm i -D webpack-dev-server
정말 다양한 옵션이 있어서 lazy기능을 넣거나, 개발시 CORS문제를 우회하기 위해 Proxy를 설정하거나 하는 작업 등을 할 수 있다. (웹팩 공식문서 DevServer 참고)
우리는 historyApiFallback
옵션만 넣어주자. HTML5 History API를 사용하는 경우 404일 때 index.html로 리다이렉트 해주는 기능이다. SPA앱을 만들시 유용하게 사용할 수 있다.
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: ['css-loader'],
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
devServer: {
historyApiFallback: true,
},
};
webpack serve
명령어를 사용해 실행시킬 수 있다. package.json에 scripts에 추가해서 사용하자.
package.json
"scripts": {
"start": "webpack serve --mode=production",
"start:dev": "webpack serve --mode=development",
"build": "webpack --mode=production",
"build:dev": "webpack --mode=production",
},
npm run start
해보면 디폴트 포트번호인 8080을 사용해 서버가 열리고 "Hello World"를 확인할 수 있다!
좋은글 감사합니다. 그런데 CSS가 적용이 된 상태로 렌더링 되지 않았는데 혹시 이부분은 어떻게 하면 적용이 될까요