현대에 들어와서 모듈 개념으로 파일별로 구분하여 작업하고 import를 통해 가져와 의존성 트리를 만드는 것은 일반적이 되어가고 있다.
하지만, 이렇게 나누어진 모듈들을 만약 아무런 체계 없이 전부 다 분리된 상태로 배포하고 그것을 브라우저가 특정 작업마다 매번 요청해야한다면 서버에 상당한 부담을 줄 뿐더러 요청에 대해서 네트워크적인 병목현상을 일으킬 수 있다.
이런 문제를 해결하기 위해 의존성을 가진 파일들을 서로 하나로 묶어 하나의 파일로 번들링하는 기능을 가진 웹팩을 사용하게 된다.
사실 create-react-app과 같은 툴체인을 이용하면 내부에 웹팩설정이 다 되어있고, 함부로 건들지 못하도록 숨겨놨기 때문에(?!) 웹팩 자체를 마주할 기회는 잘 없다만, 그래도 특정 경우에 따라 웹팩을 실제로 만져보고 만들어야 하는 상황이 올 수가 있다.
이를 위해 미리 옵션에 대해서 친해져 볼 필요가 있다.
우선 웹팩을 설치하도록 한다
npm i webpack webpack-cli -D
webpack은 코어패키지이고, cli는 웹팩 명령어구를 해당 프로젝트에서 사용할 수 있도록 만들어주는 패키지이다. ( 터미널에서 webpack이라는 명령어를 사용하기 위해 필요함 )
웹팩의 옵션을 설정하기 위한 파일을 만든다
touch webpack.config.js
기본적은 내부 구조의 형태는 아래와 같다
// webpack.config.js
const webpack = require('webpack');
module.exports = {
mode: 'development',
entry: {
app: '',
},
output: {
path: '',
filename: '',
publicPath: '',
},
module: {
rules: []
},
plugins: [],
optimization: {},
resolve: {
modules: ['node_modules'],
extensions: ['.js', '.json', '.jsx', '.css'],
},
};
{
entry: {
app: [@babel/polyfill, 'a.js', 'b.js']
}
}
위와 같은 형태로 되어있을 경우, 번들링되는 파일은 시작점을 a.js에서 시작하여 모든 의존성을 확인해 합친 파일 1개, b.js에서 시작하여 모든 의존성을 확인해 합친 파일 1개를 두개 합쳐서 app.js라는 파일로 번들링한다.
배열이 아닌 그냥 string 경로값만 존재할 경우는 당연히 해당 경로만 탐색한다
이런 특성 덕택에, 만약 코드에서 폴리필이 필요할 경우 저렇게 배열형태로 앞에 추가를 하여주면 된다.
{
output: {
path: '/dist',
filename: '[name].[chunkhash].js',
publicPath: '/',
},
}
여기서 path는 번들링이 완료된 후, 로컬에서 해당 파일이 등록되는 경로를 의미한다.
publicPath는 배포환경에서 서버의 어떤 엔드포인트에 해당 파일이 저장되야 하는 것인지를 뜻한다.
filename은 저렇게 대괄호로 묶어야 동적으로 input의 key에 따른 이름을 가진 파일로 나누어져 만들어진다.
이름의 두번째로 들어가는 [chunkhash]는 만약 웹팩에서 번들링을 하려고 할 때 결과물이 변한 점이 없다면 이 파일을 캐시상태로 가져가서 브라우저에서 계속 사용하고 별도의 http 요청을 하지 말라는 의미의 옵션이다.
예를들어, 가장 기본적으로 웹팩에서 js 파일을 번들링을 할 때, 하위 브라우저를 지원하기 위해 트랜스파일링을 하려는 의도로 babel-loader를 필수적으로 설치하게 되는데, 그때 형태는 아래와 같다.
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: '/node_modules/',
loader: 'babel-loader',
options : {
presets: [
[
'@babel/preset-env', {
targets: { node: 'current' },
modules: 'false',
useBuiltIns: 'usage'
}
],
'@babel/preset-react',
'@babel/preset-typescript'
],
}
},
{
test: /\.css$/,
// use: ['style-loader', 'css-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(jpeg|jpg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
위 내용은 예전 create-react-app 없이 환경 구축해보기 시도에서 써놨던 설정파일을 가져온 것이다.
위와 같이, rules 배열 안에 test 로 해당 파일의 확장자를 확인한 후, 그것이 exclude에 있는 내용이 아니라면 loader를 이용해서 번들링해라는 뜻이 된다.
물론, 해당 로더들은 미리 설치가 되어있어야 한다.
string 형태로 되어있다면 알아서 node_modules에서 패키지를 확인하고, 따로 설정해야한다면 commonJS 모듈 형태로 require하여 변수에 담아둔 후 use에 설정해주면 된다.
여기에 options로 들어가는 내용은 추가적으로 번들링을 하면서 제약을 할 추가 사항들이다.
위에서 보는것처럼 어떤 이름으로 번들링할지에 대한 name, 초기 번들링 환경에 대한 presets가 이와 같다.
presets는 말 그대로 번들링을 위한 환경을 뜻한다. 이 내용을 베이스로 진행하게 되는데, 예를들어 배열 내에 @babel/preset-react 가 존재한다면 바벨 로더를통해 트랜스파일을 할 때 react의 프리셋을 따라 트랜스파일을 해달라는 뜻이 된다.
react는 jsx문법을 사용하기에, 이것을 일반 자바스크립트가 이해할 수 있는 언어로 변경하는 과정을 이 preset-react가 하는 것이다.
preset 옵션 자체를 배열로 묶어 옵션들을 세부적으로 설정이 가능하다. 여기서 target은 해당 preset이 어떤환경을 타겟으로 해서 지원해야하는지를 뜻한다. babel의 경우 preset-env가 설정되면 자동으로 브라우저 환경에 따라 필요한 폴리필을 추가해주는 마법을 보여준다.
세부 옵션배열의 modules는 항상 false로 해두어야 트리쉐이킹이 된다
useBuiltIns는 항상 usage를 해두어, 사용코드를 보고 알아서 폴리필을 추가하도록 요청한다.
{
plugins: [
new HtmlWebpackPlugin({ template: PUBLIC_INDEX }), // 빌드한 결과물을 HTML 파일로 생성해주는 Plugin
new Dotenv(), // .env에 있는 변수를 가져오는 Plugin
new CleanWebpackPlugin(), // 성공적으로 다시 빌드 한 후 webpack의 output.path에있는 모든 빌드 폴더를 제거 및 정리
new MiniCssExtractPlugin() // 별도로 css 파일을 만들어서 빌드하는 Plugin
],
}