
여러분이 영어시험을 앞두고 공부한다고 생각해봅시다. 일반적으로 아래와 같은 순서로 공부를 할 것입니다.
- 시험볼 책을 준비한다.
- 시험범위가 어딘지 확인한다.
- 책을 읽는다.
- 모르는 내용을 다른 참고서 혹은 인터넷으로 검색한다.
시험공부를 예시로 든 이유는 Webpack이 최종 Build 결과물을 완성하기까지의 과정이 모두 담겨있기 때문입니다. 아래 순서를 보며 이전 내용을 복습해봅시다.
- 개발 진행 : 코드를 준비한다.
- Entry : Webpack이 정리해야 하는 범위를 확인한다.
- Loader : Webpack에게 글을 가르쳐준다.
- Plugin : 모르는 내용에 대해 참고서를 준비한다.
저는 이렇게 이해하면 Bundle 과정이 쉽게 와닿았습니다. 여러분은 어떠신가요?
그럼 Loader에 이어서 마지막 관문인 Plugin에 대해 알아봅시다. 위에서 설명한 것처럼 Plugin은 Webpack이 미처 배우지 못한 내용 혹은 기능을 대신 수행하고, 개발자가 원하는 결과를 얻을 수 있도록 도와주는 라이브러리입니다. 설정은 plugins key에 매핑하여 사용합니다.
plugins: [
사용할 plugin 객체 1,
사용할 plugin 객체 2,
사용할 plugin 객체 3,
]
여러분이 create-react-app을 이용해 개발을 해왔다면 자연스레 사용해왔던 기능들이 대부분 Plugin의 힘입니다. 대표적으로 사용되는 Plugin에는 HotLoading 혹은 HotModule이라고도 불리는 Reloading Plugin이 있습니다. webpack-dev-server를 동작시키고 일부 코드를 수정하고 저장하면 어떻게 되나요? 브라우저는 바로 바뀐 정보를 반영하기 위해 다시 렌더링을 진행합니다. 이 기능을 제공하는 것이 HotModule Plugin입니다. 보통 배포환경이 아닌 개발환경에서 많이 설정하는 Plugin입니다. 설정은 아래와 같이 진행합니다.
plugins: [
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin()
]
html-webpack-plugin은 말 그대로 html과 webpack의 관계에서 동작하는 plugin입니다. 이전 loader 글을 읽으셨다면 무언가 이상한 점이 있지 않으셨나요? 바로 loader 중에 html-loader가 존재하지 않는다는 것입니다. 왜 html-loader는 존재하지 않을까요? React를 기준으로 보았을 때, React는 html 파일 내부에 dom 하나를 연결하고 이 dom을 조작하며 화면을 렌더링합니다. 이 말은 코드가 삽입되는 html은 하나 뿐이라는 것입니다. 그래서 webpack은 굳이 loader를 통해 html 파일들을 bundle 할 필요도 없는 것입니다.
이제 html-webpack-plugin의 기능이 감이 오시나요? 네. html-webpack-plugin은 webpack의 bundle 결과물을 html 파일에 연결해주는 역할을 수행합니다. 크게 사용할 html 파일의 위치, 파일의 정보, 파일 압축 여부 등을 설정할 수 있습니다.
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin(
Object.assign({},
{
filename: 'index.html', // output file name
template: `public/index.html`, // file 위치
inject: true // bundle 결과물 삽입
},
env.isProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}
: undefined
)
),
]
interpolate-html-plugin는 package script에서 선언한 정보를 html 파일에서 사용할 수 있도록 해주는 plugin입니다. create-react-app을 처음 생성하고 index.html을 열어보면 아래와 같은 코드가 있을 것입니다.
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
주의깊게 보지 않았다면 보이지 않았을 수 있지만 참으로 이상하게 생겼습니다. 저 PUBLIC_URL은 대체 어디서 온 것일까요? 바로 package.json의 script에서부터 생겨난 정보입니다. 물론, ejecting 후 webpack 내에서 추가적으로 선언할 수도 있습니다. package.json의 script를 한번 볼까요?
{
"scripts": {
"start": "PUBLIC_URL=https://www.google.com react-scripts start",
"build": "PUBLIC_URL=https://www.google.com react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
}
위처럼 설정해주면 html에서 설정되어 있는 PUBLIC_URL은 이제 google 홈페이지를 가리키게 됩니다. 이것을 가능하게 해주는 Plugin이 interpolate-html-plugin 입니다. 설정은 아래와 같이 할 수 있습니다.
const InterpolateHtmlPlugin = require('interpolate-html-plugin');
plugins: [
new InterpolateHtmlPlugin({
PUBLIC_URL: 'https://www.google.com
}),
]
DefinePlugin은 interpolate-html-plugin과 비슷하게 package.json의 script 내용을 우리의 js 코드 안에서 사용할 수 있도록 해주는 Plugin 입니다. 혹시 process.env를 사용해본 적이 있나요? 흔히 build 타입을 결정지을 때 주로 사용하곤 하죠. 예를 들어, if (process.env.BUILD_TYPE === 'development) 등으로 사용합니다. 설정은 아래와 같이 합니다.
plugins: [
new webpack.DefinePlugin({
'process.env': {
BUILD_TYPE: JSON.stringify(process.env.BUILD_TYPE),
}
}),
]
mini-css-extract-plugin 은 Build 결과물 중 css 파일들을 정리를 도와주는 plugin 입니다. JS 파일들의 경우, webpack의 output 설정 중에 파일의 이름을 설정하여 결과물을 만들 수 있습니다. 하지만 CSS는 Webpack의 기본 기능으로는 JS 파일처럼 정리되지 않습니다. 이 때, mini-css-extract-plugin 를 사용하여 도움을 받을 수 있습니다. 설정은 아래와 같이 진행합니다.
plugins: [
new MiniCssExtractPlugin({
filename: isDevelopment ? 'styles/[name].css' : 'styles/[name].[chunkhash].css',
chunkFilename: isDevelopment ? 'styles/[id].css' : 'styles/[id].[chunkhash].css'
}),
]
더 다양한 Plugin이 많지만 이번 글에서는 개발 시 유용한 Plugin에 대해 먼저 알아보았습니다. 앞으로 차차 유용한 Plugin에 대해 소개하겠습니다. 다음 글에서는 개발이 아닌 배포를 위한 Build 결과물을 생성할 때 유용한 optimization 설정에 대해 알아보겠습니다.