들어가기전
React는 현재도 계속해서 업데이트가 되고 있는 오픈소스 라이브러리이다.
- React가 버전 18로 업데이트가 되면서 많은 부분이 바뀌었는데, 가장 빠르게 알아볼 수 있는 변화는 콘솔 창에 이전에 보이지 않던 경고문이 보인다.
- React 18 버전은 더이상 ReactDOM.render를 지원하지 않는다는 내용이다.
- 저런 경고문이 보이는 이유는 React 18에서는 이제 createRoot API를 사용하기 때문이기 때문인데
React 18에서 생긴 자동 배칭 또한 이 createRoot API를 사용해야한다.//React 18이전의 index.js const rootElement = document.getElementById("root"); ReactDOM.render(<AppTest />, rootElement); // //React 18 의 바뀐 index.js import { createRoot } from "react-dom/client"; const rootElement = document.getElementById("root"); const root = createRoot(rootElement); root.render( <App /> );
- 지금까지는 Concurrent Mode라고 명명되었지만 React 18버전 부터는 Concurrent Feature로 바뀌면서 하나의 기능으로 들어오게 되었다.
- 자바스크립트는 싱글 스레드 언어이기 때문에 원래 하나의 작업을 수행할 때 다른 작업 동시에 수행할 수 없으나, concurrent mode를 사용하면 여러 작업을 동시에 처리할 수 있었다.
- React 18 버전부터는 Concurrent Feature로 바뀌어 하나의 기능으로 들어오게 되었고, 해당 기능 중 하나가 Suspense 기능이다.
- React 18 버전부터는 이 Suspense 기능을 이용해 독립적으로 렌더링을 할 수 있도록 했다.
React는 SPA(Single-Page-Application)인데, 사용하지 않는 모든 컴포넌트까지 한 번에 불러오기 때문에 첫 화면이 렌더링 될때까지의 시간이 오래걸린다.
그래서 사용하지 않는 컴포넌트는 나중에 불러오기 위해 코드 분할 개념을 도입했다.
/* 기존에는 파일의 최상위에서 import 지시자를 이용해 라이브러리 및 파일을 불러왔다. */
import moduleA from "library";
form.addEventListener("submit", e => {
e.preventDefault();
someFunction();
});
const someFunction = () => {
/* 그리고 코드 중간에서 불러온 파일을 사용했다. */
}
그러나 이제는 구문 분석 및 컴파일해야 하는 스크립트의 양을 최소화 시키기 위해 dynamic import 구문을 지원한다.
form.addEventListener("submit", e => {
e.preventDefault();
/* 동적 불러오기는 이런 식으로 코드의 중간에 불러올 수 있다. */
import('library.moduleA')
.then(module => module.default)
.then(someFunction())
.catch(handleError());
});
const someFunction = () => {
/* moduleA를 여기서 사용 */
}
dynamic import를 사용하게 되면 불러온 moduleA 가 다른 곳에서 사용되지 않는 경우, 사용자가 form을 통해 양식을 제출한 경우에만 가져오도록 할 수 있다.
dynamic import는 then 함수를 사용해 필요한 코드만 가져온다.
가져온 코드에 대한 모든 호출은 해당 함수 내부에 있어야 한다.
이 방식을 사용하면 번들링 시 분할된 코드(청크)를 지연 로딩시키거나 요청 시에 로딩할 수 있다.
dynamic import는 React.lazy 와 함께 사용할 수 있다.
React.lazy 함수를 사용하면 dynamic import를 사용해 컴포넌트를 렌더링할 수 있다
import Component from './Component';
/* React.lazy로 dynamic import를 감싼다. */
const Component = React.lazy(() => import('./Component'));
Router로 분기가 나누어진 컴포넌트들을 위 코드처럼 lazy를 통해 import하면 해당 path로 이동할때 컴포넌트를 불러오는데 이 과정에서 로딩하는 시간이 생긴다.
/* suspense 기능을 사용하기 위해서는 import 해와야 합니다. */
import { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
{/* 이런 식으로 React.lazy로 감싼 컴포넌트를 Suspense 컴포넌트의 하위에 렌더링한다. */}
<Suspense fallback={<div>Loading...</div>}>
{/* Suspense 컴포넌트 하위에 여러 개의 lazy 컴포넌트를 렌더링시킬 수 있다. */}
<OtherComponent />
<AnotherComponent />
</Suspense>
</div>
);
}
앱에 코드 분할을 도입할 곳을 결정하는 것은 사실 까다롭기 때문에, 중간에 적용시키는 것보다는 웹 페이지를 불러오고 진입하는 단계인 Route에 이 두 기능을 적용시키는 것이 좋다
import { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
라우터에 Suspense를 적용하는 방법
- 라우터가 분기되는 컴포넌트에서 각 컴포넌트에 React.lazy를 사용하여
import
한다.- Route 컴포넌트들을 Suspense로 감싼 후 로딩 화면으로 사용할 컴포넌트를 fallback 속성으로 설정해준다.
- 주의사항
초기 렌더링 시간이 줄어드는 분명한 장점이 있지만,
페이지를 이동하는 과정마다 로딩 화면이 보여지기 때문에 서비스에 따라서 적용 여부를 결정해야 한다.