관련 글
CRA 없이 React + TypeScript + Webpack 프로젝트 만들기 #1
CRA 없이 React + TypeScript + Webpack 프로젝트 만들기 #2
어제까지 설정했던 Webpack을 기반으로 CRA로 작성한 프로젝트를 Webpack으로 변경했다.
그 과정에서 추가 설정을 했거나, 삽질했던 내용을 적어둔다.
이번 프로젝트의 아이콘은 모두 SVG였지만 일부 이미지가 PNG였다. PNG를 처리하는 설정을 webpack.config.js에 추가한다.
{
test: /\.png$/,
loader: 'file-loader',
options: {
name: 'images/[name].[ext]?[hash]',
},
},
SVG를 처리했을 때와 동일하게 src/types/custom.d.ts 파일에 png 확장자를 처리할 수 있는 선언을 추가한다.
declare module '*.svg' {
import React = require('react');
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
다음과 같이 사용할 수 있다.
import React from 'react';
import waterdropIconUrl, { ReactComponent as WaterdropIcon, } from './images/ic-btn-emo-waterdrop.svg';
import partyUrl from './images/party-8-240.png';
export default function App() {
return (
<div>
React + TypeScript + Webpack!
<div>
<img src={waterdropIconUrl} alt="Waterdrop Icon" />
</div>
<WaterdropIcon />
<div>
<img src={partyUrl} alt="Party Icon" />
</div>
</div>
);
}
production mode로 Webpack을 실행하여 bundle의 크기를 확인해보자.
Asset Size Chunks Chunk Names
app.js 478 KiB 0 [emitted] [big] app
최적화를 할 여지가 있을지 webpack-bundle-analyzer로 확인해볼 수 있다.
yarn add -D webpack-bundle-analyzer
webpack.config.js 에 추가한다.
new BundleAnalyzerPlugin({
openAnalyzer: false,
analyzerMode: 'static',
reportFilename: '../../report.html',
}),
openAnalyzer를 false로 설정하면 BundleAnalyzerPlugin가 실행된 후에 바로 브라우저에 결과가 출력되지 않도록 한다.
analyzerMode, reportFilename을 위와 같이 설정하면 report.html 파일이 지정된 경로에 생성된다.
report.html 파일을 브라우저로 확인해보면 다음과 같다.
react-spinners가 크게 비중을 차지하고 있다. 실제로 사용되는 코드를 확인해보자.
import { BeatLoader } from 'react-spinners';
이 코드를 다음과 같이 변경하자.
import BeatLoader from 'react-spinners/BeatLoader';
위와 같이 코드를 수정한 후 다시 webpack을 실행해보자.
Asset Size Chunks Chunk Names
app.js 418 KiB 0 [emitted] [big] app
size가 약 60Kb 줄어들었고 이미지에서 볼륨이 작아진 것을 알 수 있다.
axios 같은 library는 bundle에서 별도로 분리할 수 있다.
Webpack의 externals 키워드와 copy-webpack-plugin을 활용한다.
yarn add -D copy-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
templateParameters: {
env: process.env.NODE_ENV === 'production' ? '' : '[DEV]',
},
minify:
process.env.NODE_ENV === 'production'
? {
collapseWhitespace: true,
removeComments: true,
}
: false,
}),
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin({
openAnalyzer: false,
analyzerMode: 'static',
reportFilename: '../../report.html',
}),
new CopyPlugin([
{
from: './node_modules/axios/dist/axios.min.js',
to: './axios.min.js',
},
]),
],
externals: {
axios: 'axios',
},
위와 같이 설정한 후 src/index.html 파일에 axios를 로드하는 코드를 추가한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document<%= env %></title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/axios.min.js"></script>
</body>
</html>
webpack을 다시 실행하면 bundle size가 더 줄어든 것을 확인할 수 있다.
Asset Size Chunks Chunk Names
app.js 405 KiB 0 [emitted] [big] app
용량이 약 13kb 줄어들었고 이미지에서 axios는 사라졌다.
externals로 의존성을 분리하는 것은 확인해보지는 못했지만 더 다양한 기능이 있는 것 같다.
index.html에 수동으로 axio를 추가하지 않는 방법도 있을 것 같은데…
아직 배포까지는 진행하지 않았지만 로컬에서 CRA를 Wabpack으로 대체하는 과정이 잘 처리되어서 기쁘다.
최적화 방법이 더 있다는 것을 자료를 찾아보면서 확인했지만 일단은 여기까지만 적용하려고 한다.
앞으로 개인 프로젝트를 할 때는 CRA를 쓰지 않고 Webpack 써야지~