[리액트] 컴포넌트 레벨에서 웹팩으로 코드 스플리팅하기

심재철
6 min readFeb 19, 2020

--

보통 웹팩으로 개발하게되면, node_modules에 설치된 외부 모듈들을 vendor.js로 묶고 직접 개발한 코드를 또 다른 하나의 파일로 묶습니다.(이하 main.js라고 부르겠습니다.)

이렇게 하는 이유는 프로젝트에서 사용하는 외부 모듈에 변경이 없을때 vendor.js가 변경되지 않아서 유저는 브라우저에 캐시해뒀던 vendor.js를 재사용할 수 있기 때문입니다. 그럼 서버 리소스가 절약 되겠죠. 이렇게 분리된 파일을 청크파일이라고 부릅니다.

하지만 이렇게 외부모듈을 vendor.js로 분리하는것만으로는 부족합니다. 어플리케이션의 규모가 크면 분리된 main.js도 크기 때문이죠. 유저가 처음 페이지에 접속했을때 매우 큰 main.js를 로드하고 해석하는데 많은 시간이 걸리기 때문에 빠른 초기 페이지 로딩을 위해서는 이 main.js를 좀 더 잘게 분리해줄 필요가 있습니다.

예를들어, redux-form이나 react-modal같은 라이브러리는 모든 페이지에서 사용되지 않음에도 불구하고 main.js에 같이 번들링 되어 있어 초기 페이지 로딩을 느리게 만드는 주 원인이 됩니다. 이런 두개의 라이브러리들은 별도의 js파일로 분리해서 필요할때 로딩해주면 훨씬 좋겠죠.

경로별로 코드 스플리팅 하기

코드 스플리팅에는 여러가지 방법이 있습니다. 그 중 첫번째로 웹팩의 require.ensure과 react-router의 dynamic routing을 사용해서 접속 경로별로 청크파일을 불러올 수 있는 방법에 대해서 살펴보겠습니다.

1. 웹팩의 require.ensure()

require.ensure([디펜던시], function(require) {     
require('./test.js');
}, 'thisistest' );

프로젝트 어딘가에 위 코드를 적어두면 웹팩이 이 함수 호출을 만났을때 thisistest라는 이름의 청크파일을 만들어줍니다. 그리고 특정 페이지에 진입했을때 이 thisistest 청크 파일을 불러오고 싶으면 react-router의 getComponent와 연동 시켜 주면 됩니다.

이처럼 /test에 접속했을때 require.ensure를 호출해서 파일을 동적으로 불러올 수 있습니다.

2. es6 dynamic import

요즘엔 위처럼 require.ensure를 사용하지 않고 다이나믹 임포트 방법을 사용합니다. import를 함수로 사용하게 되면 웹팩이 해당 경로에 맞는 컴포넌트를 청크파일로 알아서 분리해줍니다. 그리고 promise를 리턴하기 때문에 async await과 같이 사용할 수 있습니다.

async handleClick = () => {
const modal = await import('./Modal');
this.setState({
Modal : modal.default
})
}

위 처럼 해줄 경우 Modal 컴포넌트는 청크파일로 분리되어서 버튼이 클릭되어 handleClick이 실행될때 동적으로 로드됩니다. 이렇게 불러온 modal을 state로 관리해서 렌더링해주면 됩니다. 위에서 사용한 방법보다 훨씬 간편하죠?

SSR를 고려하지 않는 경우 React의 내장 함수인 lazy와 내장 컴포넌트인 Suspense를 사용하면 좀 더 편리하게 컴포넌트를 화면에 렌더링 할 수 있습니다.

먼저, React.lazy()는 컴포넌트가 렌더링 되는 시점에 파라미터로 넘어온 함수를 호출시켜줍니다.

const Split = React.lazy( () => import('./Split') );

이렇게 해주면 컴포넌트 렌더링 시점에 Split이라는 컴포넌트를 동적으로 가져올 수 있는거죠. 이렇게 가져온 컴포넌트를

<Suspense fallback={<div>로딩중입니다</div>}>
<Split />
</Suspense>

Suspense라고 하는 리액트의 내장 컴포넌트안에 child로 넣어주면 로딩중에는 fallback에 넘겨진 컴포넌트를 렌더링해주고 로딩이 완료되면 Split컴포넌트를 보여주게 됩니다.

하지만 React의 lazy 내장 함수와 Suspense 내장 컴포넌트는 서버사이드렌더링을 지원하지 않습니다. SSR을 고려하여 코드 스플리팅을 해야 한다면 loadable components라고 하는 라이브러리를 사용해야 합니다.

오히려 위 방법보다 Loadable components를 사용하는것이 기능도 다양하고 사용법도 직관적이어서 더 좋은것 같습니다.

const Split = loadable(() => import('./Split'));

React.lazy에서 해줬던 형태와 비슷합니다.

const Split = loadable(() => import('./Split'), {
fallback : <div>로딩중입니다...</div>
}
);

아까는 fallback을 Suspense컴포넌트로 지정해줬는데 loadable component에서는 이런식으로 두번째 인자에 전달해주면 됩니다. 한번에 fallback 컴포넌트까지 설정할 수 있어서 더 편한 것 같습니다.

const onMouseOver = () => {
Split.preload();
}

이렇게 해줄 경우, 어떤 컴포넌트에 마우스를 올리자마자 컴포넌트 로딩을 시작하게 할 수 도 있습니다. 이렇게 하면 사용자는 컴포넌트가 더 빨리 로드 되는거처럼 느끼겠죠.

정리

Split 컴포넌트를 jsx안에 놓기만 하면 해당 jsx가 화면에 렌더링 될 때 Split 컴포넌트가 포함된 청크파일을 동적으로 불러와서 렌더링 하게 되는 프로세스입니다. 리액트 공식문서에서도 SSR을 고려할때 이 loadable component를 사용하라고 권장하고 있습니다. 공식적으로 인정된 라이브러리인만큼 안심하고 사용해도 좋을 것 같습니다.

출처

velopert : 리액트를 다루는 기술

Sign up to discover human stories that deepen your understanding of the world.

--

--

No responses yet

Write a response