웹팩이란 최신 프론트엔드 프레임워크에서 가장 많이 사용되는 모듈 번들러로, 웹 애플리케이션을 구성하는 자원(HTML, CSS, JavaScript)을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구이다.
의존성을 분석해 모듈을 번들시켜주는 역할을 하며, 프로젝트를 개발할 때 사용하는 수많은 라이브러리들을 빌드(build) 과정을 통해 하나의 파일로 만들어준다.
웹팩이 등장한 이유
트랜스파일러 : 한 언어로 작성된 소스 코드를 비슷한 수준의 추상화를 가진 언어로 변환하는 것을 말하며, 컴파일러의 일종이다.
바벨을 사용하는 이유는?
자바스크립트는 웹 브라우저, NodeJS 등 수많은 환경에서 실행되며, 실행되는 환경의 버전에 따라서도 영향을 받는다. 특정 버전 이상에서만 실행되는 코드가 있고, 특정 브라우저에서는 실행되지 않는 코드도 있다. 그렇기 때문에 모든 자바스크립트 실행 환경에서 정상적으로 동작할 수 있도록 하려면 바벨이 필요하다.
예) ES6 이후 버전의 자바스크립트를 대부분의 브라우저에 호환시키기 위해서 바벨을 이용해 호환 가능한 ES5 버전으로 낮춘 다음 사용하는 경우
변환 전
// 화살표 함수 (ES6)
// 지수 연산자 (ES7)
[1, 2, 3].map(n => n ** n);
변환 후
// ES5
"use strict";
[1, 2, 3].map(function (n) {
return Math.pow(n, n);
});
다음과 같이 바벨의 기본 모듈들을 설치해준다.
$npm i -D @babel/core @babel/@cli @babel/preset-env
각 모듈은 다음과 같은 역할을 한다.
@babel/core
: 바벨의 핵심 기능들을 포함@babel/cli
: 터미널에서 바벨 명령어를 사용할 수 있게 도와줌@babel/preset-env
: 코드 구문 변환 설정을 도와줌 (지원 브라우저 점유율, 호환성 설정 등)이제 추가적으로 내가 사용하고자 하는 최신 문법 플러그인을 설치해야 한다.
예를 들어 옵셔널 체이닝이라는 문법을 사용하고자 할 경우, 바벨 공식문서의 플러그인 메뉴로 이동하면 해당 플러그인을 설치하는 방법과 바벨에 적용하는 방법 등이 자세히 나와있다.
공식 문서를 따라 플러그인을 설치하고 설정을 적용시킨다.
@npm i -D @babel/plugin-proposal-optional-chaining
babel.config.json
{
"presets": [
[
"@babel/env",
{
"targets": "> 2%, not dead"
}
]
],
"plugins": ["@babel/plugin-proposal-optional-chaining"]
}
위와 같이 babel.config.json
파일을 생성해 적용하면 된다. presets
에는 바벨에 대한 설정을 넣을 수 있는데, 다음 코드는 전세계 2% 이상의 점유율을 가진 브라우저에서 동작 가능하도록 설정한 옵션이다.
이런 식으로 브라우저 점유율을 통해 설정할 수도 있고, 각 브라우저(크롬, 사파리 등)마다 버전을 지정해 설정해줄 수도 있다.
이제 웹팩을 설치해 바벨과 연결한다. 바벨만 따로 사용할 수도 있지만 웹팩과 연결하면 바벨이 코드들을 트랜스파일링하면서 웹팩을 통해 모듈들을 번들링할 수 있기 때문에 굉장히 효율적이다.
다음과 같이 웹팩과 css나 이미지 등을 사용할 수 있게 도와주는 모듈들을 설치한다.
$npm i -D webpack webpack-cli webpack-dev-server
$npm i -D babel-loader css-loader file-loader
$npm i -D html-webpack-plugin mini-css-extract-plugin
$npm i -D sass sass-loader
각 모듈은 다음과 같은 역할을 한다.
webpack
: 웹팩 모듈webpack-cli
: 터미널에서 웹팩 명령어를 사용할 수 있게 도와줌webpack-dev-server
: nodemon
과 같이 웹팩 환경에서 개발 서버를 생성babel-loader
: 웹팩과 바벨을 연동css-loader
: 웹팩이 css파일을 읽을 수 있도록 도와줌file-loader
: 웹팩이 파일을 로딩할 수 있도록 도와줌 (이미지를 로딩할 때 사용)html-webpack-plugin
: 번들링된 html에 css와 js파일들을 추가해줌mini-css-extract-plugin
: style-loader
를 대체하며 html 내의 style태그 대신 별도의 css파일로 생성해줌sass-loader
: 웹팩이 sass파일을 읽을 수 있도록 도와줌public/index.html
웹팩에서 기준으로 사용할 html 파일을 생성해준다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
src/index.js
웹팩에서 기준으로 사용할 js 파일도 생성해준다.
const root = document.getElementById('root');
root.innerHTML = '웹팩 설정하기';
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const prod = process.env.NODE_ENV === 'production';
module.exports = {
// 모드에 따라 웹팩에서 내장 최적화 제공
mode: prod ? 'production' : 'development',
// 소스 맵 생성 여부 및 방법 설정
devtool: prod ? 'hidden-soure-map' : 'eval',
// 번들링을 시작할 파일
entry: {
index: './src/index.js',
},
// 다양한 모듈들(js, css, image 등)을 처리하는 방법 결정
module: {
rules: [
{
// 처리할 모듈 형식 결정
test: /.js$/,
// 이 모듈에 사용할 loader
use: 'babel-loader',
// 제외할 파일들
exclude: /node_modules/,
},
{
test: /\.(sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '/dist/static/style/',
},
},
'css-loader',
'sass-loader',
],
},
{
test: /\.(png|jpg|svg)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'static/images',
},
},
],
},
],
},
// 빠르게 개발할 수 있도록 개발서버 제공
devServer: {
historyApiFallback: true,
inline: true,
port: 3000,
hot: true,
publicPath: '/',
},
// 번들링 된 파일이 생성될 위치 설정
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'static/js/bundle.js',
},
// 적용할 플러그인 목록
plugins: [
new HtmlWebpackPlugin({ template: `./public/index.html` }),
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({ filename: 'static/style/main.css' }),
],
};
mode
: production
, development
, node
세 가지 옵션을 사용할 수 있는데, 사용한 옵션에 따라 웹팩에서 내부적으로 최적화를 해준다. 보통 개발시에는 development
, 배포시에는 production
을 사용한다.
devtool
: 소스맵 생성 스타일을 결정할 수 있다. 여러 옵션들이 있으며 배포시에는 소스맵을 숨기는 것이 좋으므로 hidden-soure-map
을 사용한다.
entry
: 번들링을 시작할 파일을 결정할 수 있다.
module
: 다양한 모듈들을 처리하는 방법들을 결정한다. js파일, ts파일을 포함한 이미지 파일, 스타일 파일 등 웹팩을 통해 번들링되는 모든 파일들의 처리 방법을 설정하며, 바벨 또한 이 곳에서 설정한다. module을 설정할 때 중요한 부분은 loader를 읽을 때 오른쪽에서 왼쪽으로 loader가 실행되기 때문에 sass-loader
의 경우 css-loader
보다 오른쪽에 위치시켜야 한다. (typescript의 경우에는 babel-loader
오른쪽에 ts-loader
를 위치시켜야 한다.)
devServer
: 개발 서버에 대한 설정을 할 수 있다. 에러처리, 포트 설정, 기본 path 등 여러 옵션을 설정할 수 있다.
output
: 웹팩을 통해 최종적으로 번들링된 파일을 저장할 위치를 설정한다.
plugins
: 웹팩에 적용할 플러그인들을 설정한다.
이제 프로젝트에서 웹팩을 실행시키기 위한 명령어를 설정한다.
package.json
다음과 같이 개발서버를 실행하기 위한 dev 명령어와 빌드를 위한 build 명령어를 설정해준다.
"scripts": {
"dev": "webpack-dev-server --open --hot",
"build": "NODE_ENV=production webpack --mode production --env=build"
}
style.scss
설정한 이미지, sass가 모두 동작하는지 확인하기 위해 이미지 파일과 sass 파일을 추가한다.
// 배경색을 민트색으로 지정
body {
background-color: #b2dfdb;
}
src/index.js
scss 파일과 이미지 파일이 적용되는지 확인하기 위해 사진을 하나 넣어준다.
import './style.scss';
import cat from './cat.jpg';
const root = document.getElementById('root');
const img = document.createElement('img');
img.src = cat;
img.alt = 'cat';
root.appendChild(img);