01.18 Code Splitting 0


React.js

01.18. 리액트 코드 스플리팅

싱글 페이지 애플리케이션단점은 페이지 로딩 속도지연될 수 있다는 것이다. 로딩 속도가 지연되는 이유는 자바스크립트 번들 파일에 모든 애플리케이션로직을 불러오므로 규모가 커지면서 용량도 커지기 때문이다. 하지만 이 문제는 코드 스플리팅(code splitting)을 하면 해결할 수 있다.

코드 스플리팅(code splitting)은 말 그대로 코드를 분할한다는 의미이다. webpack에서 프로젝트를 번들링할 때 파일 하나라 아니라 여러 개로 분리시켜서 결과물을 만들 수 있다. 또 페이지를 로딩할 때 한꺼번에 불러오는 것이 아니라 필요한 시점에 불러올 수도 있다.

01.18.1. 코드 스플리팅의 기본

webpack4 이전의 버전에서는 vendor 를 직접 설정해야 했지만, webpack4 부터는 자동으로 생성해주기 때문에 별도의 설정은 하지 않아도 된다.

01.18.1.1. 비동기적 코드 불러오기: 청크생성

페이지에서 필요한 코드들만 불러오려면, 청크(chunk)를 생성해야한다. 청크를 생성하면 페이지를 로딩할 때 필요한 파일만 불러올 수 있고, 아직 불러오지 않은 청크 파일들은 나중에 필요할 때 비동기적으로 불러와 사용하라 수 있다.
// src/components/SplitMe.js
import React from 'react';

const SplitMe = () => {
return (
<h3>청크</h3>
);
};

export default SplitMe;
청크를 생성할 컴포넌트 자체는 특별히 하는 것이 없다. 다만 이 컴포넌트를 불러오는 것이 평상시와는 조금 다르다. 비동기적으로 파일을 불러오려면 import 를 코드 최상단에 적는 것이 아니라, 특정 함수 내부에서 작성한다. LifeCycle 메서드 안에 넣을 수도 있고, 별도의 이벤트를 설정하여 불러오도록 설정할 수도 있다.

SplitMe 를 비동기적으로 불러올 AsyncSplitMe 컴포넌트를 만들어 버튼을 눌렀을 때, SplitMe 컴포넌트를 불러와 state 에 담고 이를 렌더링한다.
// src/components/AsyncSplitMe.js
import React, {Component} from 'react';

class AsyncSplitMe extends Component {
state = {
SplitMe: null
}

loadSplitMe = () => {
// 비동기적으로 코드를 불러온다. 함수는 Promise 를 결과로 반환한다.
// import() 는 모듈의 전체 네임스페이스를 불러오므로, default 를 직접 지정해야한다.
import('./SplitMe').then(({ default: SplitMe }) => {
this.setState({
SplitMe
});
});
}

render() {
const { SplitMe } = this.state;
// SplitMe 가 있으면 이를 렌더링하고, 없으면 버튼을 렌더링한다.
// 버튼을 누르면 SplitMe 를 불러온다.
return SplitMe ? <SplitMe /> : <button onClick={this.loadSplitMe}>SpdlitMe 로딩</button>
}
}

export default AsyncSplitMe;

// src/App.js
import React from 'react';
import {Route} from 'react-router-dom';

import {Home, About, Posts} from 'pages';
import Menu from 'components/Menu';

import AsyncSplitMe from 'components/AsyncSplitMe';

const App = () => {
return (
<div>
<Menu />
<AsyncSplitMe />
(...)
</div>
);
}

export default App;
크롬 개발자 도구의 Network 탭을 열어 SplitMe 비동기적 로딩 버튼을 누르면 네트워크에는 2.chunk.js 파일을 불러온 기록이 남는다.
이렇게 코드 위에서 import __ from __ 이 아닌 import() 함수로 컴포넌트를 불러오면 webpack 은 청크를 생성하여 저장한다. 

01.18.1.2. 라우트에 코드 스플리팅

SplitMe, AsyncSplitMe 는 더 이상 사용하지 않기 때문에 삭제하고, App 에서 import 했던 코드도 삭제한다.

- asyncComponent 함수 생성
비동기적으로 불러올 코드가 많으면 청크를 생성할 때마다 파일에 비슷한 코드들을 반복하여 작성해야한다. 
조금 더 편하게 구현할 수 있도록 따로 함수화하여 재사용한다.
// 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;
this.setState({Component});
});
}

render() {
const { Component } = this.state
if (Component) {
return <Component {...this.props} />
}
return null;
};
}
};
이 함수는 컴포넌트를 import 하는 함수를 호출하는 함수를 파라미터로 받는다.
asyncComponent( () => import('./Home') );
그리고 파라미터로 받은 함수는 constructor 에서 실행하여 컴포넌트를 불러온다. 해당 컴포넌트를 실제로 렌더링할 때 파일을 불러오도록 설정한 것이다. 컴포넌트가 로딩되면 불러온 컴포넌트를 state 에 집어넣고, 또 static 값으로도 설정한다.

컴포넌트가 언마운트되었다가 나중에 다시 마운트될 때는 컴포넌트를 다시 새로 불러오지 않고, static 값으로 남아있는 이전에 불러온 컴포넌트 정보를 재사용한다.

01.18.1.3. 라우트 코드 스필리팅용 인덱스 생성

// src/pages/index.async.js
import asyncComponent from '../lib/asyncComponent';

export const Home = asyncComponent( () => import('./Home') );
export const About = asyncComponent( () => import('./About') );
export const Post = asyncComponent( () => import('./Post') );
export const Posts = asyncComponent( () => import('./Posts') );

// src/App.js
import React from 'react';
import {Route} from 'react-router-dom';

import {Home, About, Posts} from 'pages/index.async.js';
import Menu from 'components/Menu';

const App = () => {
return (
<div>
<Menu />
<Route exact path="/" component={Home} />
<Route path="/about/:name?" component={About} />
<Route path="/posts" component={Posts} />
</div>
);
};

export default App;
크롬 개발자 도구의 Network 탭을 연 상태에서 메뉴의 링크를 클릭해서 이동해보면 비동기 로딩이 처릭 될 것이다.( Posts 컴포넌트는 index.async.js 파일로 치환하지 않았기 때문에 현재는 비동기 로딩이 되지 않는다. 나중에 설정할 것이기 때문에 무시해도 된다.)

코드 스플리팅은 프로젝트 규모가 클수록 효과가 있다. 소규모 프로젝트라면 코드 스플리팅을 해 보았자 3kb 미만으로 큰 효과는 없다.
소규모 프로젝트라면 코드 스플리팅은 생략해도 된다. 나중에 파일 크기가 좀 커졌다고 느낄 때 코드 스플리팅을 구현해도 무방하다.



덧글

댓글 입력 영역