3-1. 코드 스플리팅
우리가 17장에서 배웠었던 코드 스플리팅을 이 프로젝트에서도 적용해보겠습니다.
코드 스플리팅용 라우트 인덱스 만들기
우선 컴포넌트를 비동기적으로 불러 올 수 있게 해주는 함수 asyncComponent 를 lib 디렉토리에 파일을 만들어서 작성하세요.
src/lib/asyncComponent.js
import React from 'react';
export default function asyncComponent(getComponent) {
return class AsyncComponent extends React.Component {
static Component = null;
state = { Component: AsyncComponent.Component };
constructor(props) {
super(props);
if (AsyncComponent.Component) return;
getComponent().then(({default: Component}) => {
AsyncComponent.Component = Component;
// 여기가 constructor 이긴 하지만 이 함수는 비동기적으로 작동하기에
// 실질적으로는 컴포넌트가 마운트 되고 나서 실행되기 때문에 this.state.Component = ... 가 아닌
// this.setState(...) 로 진행합니다.
this.setState({ Component });
});
}
render() {
const { Component } = this.state
if (Component) {
return <Component {...this.props} />
}
return null
}
}
}
그 다음엔 이 함수를 사용하여 비동기 라우트 인덱스 파일인 index.async.js 파일을 만드세요.
src/pages/index.async.js
import asyncComponent from 'lib/asyncComponent';
export const ListPage = asyncComponent(() => import('./ListPage'));
export const PostPage = asyncComponent(() => import('./PostPage'));
export const EditorPage = asyncComponent(() => import('./EditorPage'));
export const NotFoundPage = asyncComponent(() => import('./NotFoundPage'));
프로덕션용 웹팩 설정 변경하기
코드 스플리팅이 제대로 이뤄지도록, webpack.config.prod.js 를 수정하세요. 일단, 자주 변경되지 않는 코드인 react, react-dom, redux, axios, codemirror 등의 라이브러리들을 entry 부분의 vendor 로 추가하세요.
config/webpack.config.prod.js - entry
entry: {
app: paths.appIndexJs,
vendor: [
require.resolve('./polyfills'),
'react',
'react-dom',
'react-router-dom',
'redux',
'axios',
'codemirror',
'prismjs'
],
},
그 다음엔, 설정 파일 하단의 plugins: [ 쪽으로 스크롤하여 플러그인들은 추가하겠습니다. vendor 파일이 따로 분리되고, 중복되는 코드가 다른 파일에 들어가지 않도록 CommonsChunkPlugin 을 설정하고, “pages” 를 import 하게 되면 index.js 가 아닌 index.async.js 를 불러오도록 하기 위하여 NormalModuleReplacementPlugin 을 적용하세요.
config/webpack.config.prod.js
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
}),
new webpack.NormalModuleReplacementPlugin(
/^pages$/,
'pages/index.async.js'
),
...
벌써 코드스플리팅 작업이 완료되었습니다! 우리가 이전에 리액트 라우터를 공부 할 때 한번 다뤄봤기 때문에, 금방 할 수있지요?
한번 터미널에서 yarn build 를 입력해보세요
$ yarn build
yarn build v0.27.5
$ node scripts/build.js
Creating an optimized production build...
Compiled successfully.
File sizes after gzip:
115.69 KB build/static/js/vendor.11f64b1b.js
45.18 KB build/static/js/0.dd9800e4.chunk.js
33.96 KB build/static/js/1.4ccb9a74.chunk.js
33.84 KB build/static/js/app.6055e75f.js
23.83 KB build/static/js/2.57664346.chunk.js
1.03 KB build/static/css/app.14c573c3.css
222 B build/static/js/3.38132d94.chunk.js
(...)
위와 같은 실행결과가 나타나나요? 우리는 컴포넌트를 스타일링 하기 위하여 CSS Module 과 Sass 를 결합하여 사용했는데요, CSS Module 을 사용 할 경우에, 코드 스플리팅 과정에서 CSS 관련 코드도 스플리팅이 되어서 메인 CSS 파일인 app._.css 파일에 저장되는 것이 아니라, 각 청크파일에 들어가게 됩니다.
프로젝트에서 build 디렉토리를 보면, 우리가 방금 만든 파일들이 있는데요, 그 중에서 0._____.chunk.js 파일을 열어보시면, 실제로 CSS 관련 코드가 자바스크립트 파일 내부에 내장되어있는 것을 볼 수 있습니다. (못찾겠다면 scss 를 검색해보세요)
서버사이드 렌더링을 하지 않는다면, 이러한 구조는 전혀 문제가 되지 않습니다. 하지만, 우리가 추후 서버사이드 렌더링을 하게 된다면 자바스크립트가 실행되기 전 부터, 페이지의 내용을 HTML 로 미리 받아보게 되는데요, 이렇게 CSS 가 각 자바스크립트 안에 내장이 되어있으면, 자바스크립트가 다 불러와질 때 까지 스타일링 되지 않은 HTML 이 렌더링 되게 됩니다. 결국, 흰 페이지에 검정색 텍스트만 나타나게 되겠죠. 물론, 파일을 다 불러오고 나면 모두 정상적으로 보여지겠지만, 그때까지는 사용자의 네트워크 속도가 느리다면 몇 초 정도를 못생긴 페이지를 보여줄지도 모르게 되는것이지요.
CSS 코드가 스플리팅되는것을 방지하는 것은 매우 간단합니다.
webpack 의 플러그인 부분에 다음과 같은 코드가 보일 텐데요:
config/webpack.config.prod.js - plugins - ExtractTextPlugin
new ExtractTextPlugin({
filename: cssFilename,
}),
이것을 다음과 같이 수정해주시면 됩니다.
new ExtractTextPlugin({
filename: cssFilename,
allChunks: true
}),
위와 같이 allChunks 값을 설정해주고 나면 CSS 코드 스플리팅은 이뤄지지 않습니다. 설정파일을 저장하고 나서, 다시 yarn build 를 실행해보세요.
이번에 만들어진 파일의 사이즈를 보면 다음과 같습니다:
115.69 KB (-2 B) build/static/js/vendor.b8e93646.js
34.81 KB (-10.36 KB) build/static/js/0.ba96e82c.chunk.js
32 KB (-1.84 KB) build/static/js/app.fbeaaac0.js
28.44 KB (-5.53 KB) build/static/js/1.7e8d1912.chunk.js
20.61 KB (-3.21 KB) build/static/js/2.d4449d1e.chunk.js
4.96 KB (+3.93 KB) build/static/css/app.339db884.css
223 B (+1 B) build/static/js/3.672391ac.chunk.js
청크 파일들의 사이즈가 전체적으로 줄어들었고, CSS 파일의 사이즈가 조금 늘었습니다.