2-2. 비동기적 코드 불러오기: 청크 생성
자, 이제 코드 스플리팅의 꽃인 코드를 비동기적으로 불러오는 작업을 해봅시다. 코드를 비동기적으로 불러오면, 웹팩에서 처리를 하면서 코드를 분리시키는데요, 이를 청크(chunk) 라고 부릅니다.
자, 먼저 다음과 같이 SplitMe 라는 간단한 컴포넌트를 components 디렉토리에 만들어보세요.
src/components/SplitMe.js
import React from 'react';
const SplitMe = () => {
return (
<h3>
SplitMe
</h3>
);
};
export default SplitMe;
코드 스플리팅 할 파일 자체에는 특별히 하는게 없습니다. 하지만 이를 불러올때는 평소와 조금 다릅니다.
비동기적으로 파일을 불러오기 위해서, import
를 최상단에서 하는것이 아니라, 특정 함수에서 불러오도록 작성합니다. LifeCycle 메소드 안에 넣을수도 있고, 우리가 따로 함수를 지정해줄 수도 있겠죠.
다음 코드는 App 컴포넌트에서 SplitMe 컴포넌트를 버튼이 눌려졌을 때 비동기적으로 불러오는 코드입니다. 먼저 한번 훑어보면서 직접 작성을 해보세요.
src/App.js
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import { Home, About, Posts } from "pages";
import Menu from "components/Menu";
class App extends Component {
state = {
SplitMe: null
};
showSplitMe = () => {
// 비동기적으로 코드를 불러옵니다. 함수의 결과는 Promise 를 반환합니다.
// import() 는 모듈의 전체 네임스페이스를 불러오므로, default 를 직접 지정해주어야합니다.
import("components/SplitMe").then(({ default: Component }) => {
// 불러오고 난 다음엔 컴포넌트를 state 에 집어넣습니다.
this.setState({
SplitMe: Component
});
});
};
render() {
const { SplitMe } = this.state; // state 에 담겨있는 SplitMe 에 대한 레퍼런스를 만들고
return (
<div>
<Menu />
{SplitMe && <SplitMe /> /* SplitMe 가 유효하면 렌더링을 해줍니다 */}
<button onClick={this.showSplitMe}>ClickMe</button>
<Route exact path="/" component={Home} />
<Route path="/about/:name?" component={About} />
<Route path="/posts" component={Posts}/>
<ShowPageInfo />
</div>
);
}
}
export default App;
import()
은 Promise 를 반환하며, 해당 Promise 가 resolve 하는 값은 모듈의 전체 네임스페이스입니다.
then() 안에 들어있는 함수의 파라미터에 ({default: Component}) 가 들어갔지요? 이는 비구조화 할당 문법의 활용법입니다. 결과값 객체 안에 있는 default 를 Component 라는 이름으로 할당해주는것입니다.
다음 코드를 읽고 브라우저의 개발자 콘솔에서 직접 실행해보신다면 이해가 바로 될거에요.
cosnt foo = { hello: 'world' };
const { hello: bar } = foo;
console.log(bar) // world
App 코드를 완성하고나면, 브라우저를 열어서 개발자도구의 Network 를 열고, 버튼을 클릭해보세요.
버튼을 누르니까 파일을 새로 불러왔지요?
자.. 그런데 코드스플리팅을 할 때마다 이렇게 상태관리를 해주는건 매우 귀찮을것입니다.
이를 간편하게 하기위하여 페이스북의 개발자 Andrew Clark (acdlite) 가 공유한 코드가 있는데, 한번 살펴볼까요?
// https://gist.github.com/acdlite/a68433004f9d6b4cbc83b5cc3990c194
function asyncComponent(getComponent) {
return class AsyncComponent extends React.Component {
static Component = null;
state = { Component: AsyncComponent.Component };
componentWillMount() {
if (!this.state.Component) {
getComponent().then(({default: Component}) => {
AsyncComponent.Component = Component
this.setState({ Component })
})
}
}
render() {
const { Component } = this.state
if (Component) {
return <Component {...this.props} />
}
return null
}
}
}
const Foo = asyncComponent(() => import('./Foo'))
const Bar = asyncComponent(() => import('./Bar'))
asyncComponent
함수는 파라미터로 컴포넌트를 불러오는 함수를 받아와서 상태관리를 자동으로 해줍니다. 그리고 스플리팅할 코드를 코드의 하단에 나와있는것처럼 불러와주면 되지요.
이 코드를 살펴보면, 함수 내에서 컴포넌트를 정의하고, componentWillMount에서 불러온 컴포넌트를 static 값으로 설정합니다. 이렇게 함으로서, 한번 불러왔던 컴포넌트가 언마운트 되어도, static 으로 설정한 값은 유지되기 때문에, 나중에 컴포넌트가 마운트 될 때 초기 state 가 설정되면서 기존에 불러왔었던 컴포넌트를 사용하기 때문에 파일을 다시 새로 불러오지 않아도 계속 사용 할 수 있습니다.
자, 이제 비동기 코드로딩이 어떻게 이뤄지는지도 이해했고, 이를 쉽게 하는 방법도 알게되었습니다. 다음 섹션에서는 방금 나온 코드의 asyncComponent 를 통해 라우트들을 코드 스플리팅 하는 방법을 알아보겠습니다.
여기서 잠깐!
16.3 부터 componentWillMount 가 사라진다고 했었죠? 우리는 해당 부분은 constructor 로 구현하겠습니다~