React.lazy와 Suspense 컴포넌트를 통한 코드 스플리팅
Next를 사용하지 않고 react 자체 내에서 코드 스플리팅을 하는 방법을 알아봅시다. 코드 스플리팅을 하는 이유는, 한번에 다 부르지 말고 필요할 때만 나를 불러라!는 것입니다.
SPA에서는 필요한 코드를 처음 접속 시에 모두 클라이언트단으로 넘겨주기 때문에 사용자가 접속하지 않은 페이지에 들어가는 코드도 함께 전송됩니다. 작은 웹에서는 문제가 없지만 프로젝트의 규모가 커질 수록 문제가 됩니다.
우선 CRA를 통해 프로젝트를 생성한 뒤에 build를 해보았습니다.
2로 시작하는 파일은 라이브러리 관련 코드이며
main으로 시작하는 파일은 코더가 작성한 내용들이 들어 있습니다.
이렇게 분리되는 것은 CRA가 제공하는 기본 웹팩이 SplitChunks 기능을 사용하기 때문입니다. 라이브러리 관련 코드를 분리했기 때문에 캐싱 기능의 이점을 더 오래 누릴 수 있다는 장점이 있습니다.
그러나 SplitChunks을 통한 코드 스플리팅은 단순히 캐시에 효율이 있다 뿐이지 우리가 의도한 코드 스플리팅 효과를 볼 수는 없습니다.
즉, 우리가 의도한 Lazy한 로딩(코드 비동기 로딩)은 구현되지 않고 모든 컴포넌트가 main 하나에 저장되게 됩니다.
import 함수를 통한 비동기 로딩
react v16.6 이후부터는 코드 스플리팅을 위한 내장 함수인 React.lazy와 Suspense 컴포넌트가 생겼습니다.
그 전 버전에서는 import 함수를 통한 비동기 로딩을 하며, 클래스형 컴포넌트를 사용해야 합니다.
우선 자바스크립트 함수를 비동기 로딩해보겠습니다. 당연히 파일을 따로 분리해서 빌드할 예정이니까 같은 파일 내에 저장하면 안되겠죠
보시면 클릭할 때 import를 진행하고 던져준 promise를 받아서 함수를 실행합니다.
받은 res를 콘솔창에 출력해보시면, import한 파일의 내용 중 export한 값들이 있는 것을 확인할 수 있습니다.
export default로 함수를 내보냈으니 우리가 사용하고자 하는 notify 함수는 default에 들어 있네요.
export default function notify() {
alert("안녀어어어엉");
}
import React from "react";
import "./App.css";
function App() {
return (
<div>
<button
onClick={() => {
import("./notify").then((res) => res.default());
}}
>
눌러요
</button>
</div>
);
}
export default App;
이제 빌드해보시면 3으로 시작하는 빌드 결과물이 나온 것을 확인하실 수 있습니다.
코드 스플리팅이 구현된 것이죠.
React.lazy와 Suspense 컴포넌트
https://ko.reactjs.org/docs/code-splitting.html#reactlazy
React.lazy와 Suspense는 아직 서버 사이드 렌더링을 할 수 없습니다. 서버에서 렌더링 된 앱에서 코드 분할을 하기 원한다면 Loadable Components를 추천합니다. 이는 서버 사이드 렌더링과 번들 스플리팅에 대한 좋은 가이드입니다.
import React from "react";
function SplitMe() {
return <div>SplitMe</div>;
}
export default SplitMe;
React.lazy를 통해 다이나믹 import로 사용할 컴포넌트를 가져옵니다.
그 후, Suspense 내부에서 해당 컴포넌트를 호출하면 됩니다.
Suspense의 fallback 속성은 해당 컴포넌트를 가져오는 데 걸리는 시간 동안 렌더할 일종의 로더를 지정할 수 있습니다.
import React, { Suspense, useState } from "react";
import "./App.css";
const SplitMe = React.lazy(() => import("./SplitMe"));
function App() {
const [visible, setvisible] = useState(false);
return (
<div>
<button
onClick={() => {
setvisible(true);
}}
>
가져오기
</button>
<Suspense fallback={<div>로딩중입니다...</div>}>
{visible ? <SplitMe /> : null}
</Suspense>
</div>
);
}
export default App;
Loadable Components에 대한 내용은 다른 포스트에서 살펴보겠습니다.
참고 자료)