Webpack은 번들러이다. 번들러란 필요한 의존성을 추적하여 해당하는 의존성들을 그룹핑 해주는 도구이다.
번들링을 이해하기 위해선 우선 모듈(Module)을 알아야 한다.
모듈(Module)이란 유지보수 및 가독성의 향상을 위해 프로그램의 기능별로 각각의 파일들을 구성하는 것이다. 그러나 이렇듯 세분화된 모듈 파일이 늘어날 수록 네트워크의 코스트는 늘어날 수 밖에 없다. 웹 환경은 발전하는 컴퓨터의 성능과 관계없이 요청과 응답으로 이루어져 있기 때문에 파일이 늘어나면 늘어날 수록 요청과 응답에 걸리는 시간도 그에 비례하기 때문이다.
따라서 우리는 모듈화된 파일 구조는 유지하면서도 지정한 단위로 파일들을 하나로 만들어서 요청에 대한 응답으로 전달할 수 있는 환경을 만들어주는 것이 필요하고, 이를 수행하는 것이 바로 번들러(Bundler)이다. 대표적인 번들러로는 Webpack, Rollup, Parcel 등이 있으며, 이들은 서로 연관성 있는 여러 파일(모듈)들을 하나의 번들 파일로 묶어주는 역할을 한다. 여기에는 JS파일 뿐만 아니라 다양한 타입의 파일들이 포함된다. 또한 그 외에도 사용자 환경 최적화를 위한 다양한 기능들을 수행한다.
대표적인 번들러인 Webpack을 예로 들자면 Webpack은 production 모드를 사용시 코드 난독화, 압축, 최적화 작업을 지원하기도 하며, Webpack의 주요 구성 요소 중 하나인 로더(Loader)는 일부 브라우저에서 지원이 되지 않는 형식의 파일을(예: 타입스크립트 파일, css 파일) 변환하여 사용가능하게 한다.
번들러의 사용예시를 알아보자.
<script src="/src/App.js"></script>
<script src="/src/clock.js"></script>
<script src="/src/quotes.js"></script>
<script src="/src/background.js"></script>
<script src="/src/greetings.js"></script>
<script src="/src/weather.js"></script>
이렇게 여러개의 자바스크립트 파일을 로딩하는 예제를 보자. 위에 경우처럼 일반적인 태그 방식으로 자바스크립트를 모듈화하는 경우, 같은 스코프를 공유하게 됨에 따라 전역 변수의 충돌이 일어날 수 있고,라이브러리 로딩 순서나 복잡도에 따른 이슈가 발생할 수 있다. 이렇게 여러 개의 자바스크립트 파일을 로딩할 때 서로 다른 스코프나 의존성을 가지는 파일들을 하나로 묶어서 의존성을 관리해 주는 것을 번들러라고 한다. 즉, 번들러를 사용하면 의존성에 대한 고민 없이 파일 단위로 모듈화하는 것을 지원해주고, 이에 따라 여러 개의 파일을 하나로 묶어주기 때문에 네트워크 접속의 부담을 줄여 더 빠른 서비스를 제공할 수 있다.
사실 파일의 크기 문제에 있어서, http1.1 이후 나온 http2.0은 하나의 커넥션에 동시에 여러 파일들을 요청할 수 있지만, 아직 보편화 되지 않은데다가 여전히 파일이 적을수록 더 좋은 경우가 존재한다. 또한, 의존성 관리를 해결해주기 때문에 웹팩은 개발자에게 매우 많은 편익을 제공한다.
이제 웹팩을 설정해보자. 우선 webpack.config.js라는 파일을 만들어준다. 반드시 저 이름이어야 웹팩이 설정을 인식한다. 다른 이름으로 사용하고 싶다면
webpack --config <이름>
이로 이름을 지정해주어야 한다.
많은 설정이 있지만 모두 설명하기엔 글이 너무 길어지는 관계로 가장 중요한 entry, output, module, plugins 4가지에 대해 설명하겠다.
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: "development", //production
devtool: 'eval', //hidden-source-map
resolve: {
extensions:[ '.js', '.ts']
},
entry: {
app:'./src/index'
},
module: {
rules: [
{
test: /\.ts?$/,
exclude : /node_modules/,
loader:'ts-loader',
},
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
]
},
plugins: [
],
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
}
}
우선, node의 경로를 처리해주는 path 모듈과 webpack 모듈을 불러와야한다.
resolve는 웹팩이 알아서 경로나 확장자를 처리할 수 있게 도와주는 옵션이다. extensions에 넣은 확장자들은 웹팩에서 알아서 처리해주기 때문에 파일에 저 확장자들을 입력할 필요가 없어진다. 그리고modules에 node_modules를 넣어야 디렉토리의 node_modules를 인식하게 된다.
entry 부분이 웹팩이 파일을 읽어들이기 시작하는 부분이다.. app이 객체의 키로 설정되어 있는데 이 부분 이름은 자유롭게 설정할 수 있다. 저 키가 이름이 곧 결과물의 파일명이 된다. 즉 app이면 app.js가 파일명이 된다.
entry 파일은 여러개가 될 수도 있으며 반대로 하나의 entry파일로 묶을 파일을 여러개 지정할 수도 있다. 이 때는 배열로 감싸주면 된다.
entry: {
app:'./src/index'
another: './src/greeting'
main:['./src/hi', './src/bye']
},
Output은 결과물이 어떻게 나올지에 대한 설정이다.
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist'),
publicPath: "/public/",
}
path는 output으로 나올 파일이 저장될 경로이며. publicPath는 파일들이 위치할 서버 상의 경로이다. express의 express.static 경로와 비슷하다. filename의 '[name].js'는 앞서 entry 부분에서 키로 설정한 이름이 그대로 들어간다. path.join은 앞서 불러온 path모듈의 메서드로, 인자로 넣은 경로명들을 합쳐 준다. __dirname
은 현재 디렉토리를 뜻하므로 위의 예시는 현재 디렉토리에 위치한 dist 폴더에 결과물을 생성한다.
Loader는 웹팩의 막강한 편의기능이다. Loader에 대해서 자세히 알기 전에 우선 Loader로 가장 많이 쓰이는 Babel이 뭔지 알고 넘어가자.
Babel
바벨은 기본적으로 자바스크립트의 최신문법 및 사양을 지원하지 않는 브라우저에서도 쓸 수 있게끔 변환해주는 transcompiler이다. 바벨을 사용하기 위해 설치해야하는 패키지는 3가지가 있다.
@babel/core : 바벨의 코어 패키지
@babel/cli :커맨드라인에서 파일을 컴파일하게 해주는 CLI 제공
@babel/preset-env : 구문변환에 대한 별도의 설정없이 최신 자바스크립트를 구형 브라우저에 사용할 수 있게 해주는 스마트 사전
바벨을 통해 우리는 최신 ES6 문법 등의 최신사양을 사용하며 편리하게 코딩하고 결과물을 구형 브라우저에서도 작동되게끔 변환해줄 수 있다.
loader에서는 이러한 바벨 등의 프론트엔드 빌드 과정을 웹팩의 번들링과 함께 쓸 수 있게 해주는 것이다. 번들링을 하면서 필요한 전처리나 컴파일까지 할 수 있다니 일석이조인데, 사용하지 않을 이유가 있을까? 기본적으로 가장 많이 쓰이는 loader에는 바벨 외에도 타입스크립트를 컴파일해주는 ts-loader, css파일을 import해주는 css-loader 등이 있다.
설정은 다음과 같다.
module: {
rules: [
{
test: /\.ts?$/,
exclude : /node_modules/,
loader:'ts-loader',
},
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
test: /\.jsx?$/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
targets: { node: 'current' }, // 노드일 경우만
modules: 'false',
useBuiltIns: 'usage'
}
],
'@babel/preset-react', // 리액트를 쓴다면
'@babel/preset-typescript' // 타입스크립트를 쓴다면
],
]
},
test에 로딩할 파일을 지정해주고, user에 사용할 로더를 정해주면 된다. exclude는 제외할 폴더나 파일로, 바벨로 컴파일하지 않을 것들을 지정해준다. 보통 node_modules를 exclude한다.
babel의 loader 설정은 babel.config.json을 설정하는 것과 거의 유사하다. babel의 옵션들을 조금 더 자세하게 살펴 보자면 target은 지원하길 원하는 환경을 적는 곳이다. 현재 최신 버전 노드로 되어있는데 구 버전 노드 버전을 적어주면 구 버전 문법을 지원하기 위해 폴리필들이 추가되며, 노드 대신 브라우저를 타겟으로 할 수도 있다.
modules를 false로 해야 최신모듈 시스템이 그대로 유지되어서 트리 쉐이킹이 된다. 트리쉐이킹이란 ES2015 모듈 시스템에서 import되지 않은 export들을 정리해주는 기능으로, 용량이 많이 줄어들기 때문에 꼭 사용하는 것을 권장한다. 단, commonJS나 AMD, UMD같은 모듈 시스템을 사용해야하는 클라이언트에서는 쓰면 제대로 처리되지 않는다.
Plugin은 압축을 한다거나, 핫리로딩을 한다거나, 파일을 복사하는 등의 부수적인 작업을 위한 부가기능이다. 보다 효율적인 번들링을 위해서 사용할 수 있지만 요즘에는 많은 부분을 webpack.config.js의 optimization 설정으로 대체 가능하기 때문에 사용빈도가 줄어드는 추세다.
이상 babel과 웹팩을 알아보았다.