나는 주로 css in js를 활용해 개발을 한다. 왜냐하면 css의 style을 컴포넌트로 만들어 사용하면 React에서의 state와 props로 인해 쉽게 제어할 수 있기 때문이다. 그런데 css in js의 치명적인 단점이 있는데, 그것은 바로 성능의 문제이다. 아무래도 컴포넌트 단위로 style이 포함되다 보니 코드가 많아지고 이미 react와 결합되어 있는 형태이다 보니 렌더링 할 때 js코드를 함께 실행해야 한다. 그래서 렌더링 할 때 느리다.
이런 치명적인 단점에도 불구하고 나는 css in js의 장점이 너무 매력적이라 포기하고 싶지가 않다. 그래서 이를 극복하는 방법들에 대해 찾고 공부하던 중 발견한 것이 바로 코드 분할이다.
코드 분할 (Code Splitting)은 자바스크립트 코드를 논리적인 단위로 분할하는 것을 말한다. 이 기술을 사용하면 사용자가 페이지를 로드할 때 필요한 최소한의 코드만 다운로드할 수 있다. 이를 통해 초기 로드 시간과 브라우저가 처리해야 하는 작업 양이 줄어들어 앱의 성능이 개선된다.
React에서 코드 분할을 구현하는 일반적인 방법은 React.lazy()를 사용하는 것이다. 이를 통해 필요로 하는 컴포넌트만 다운로드하고 렌더링 할 수 있다. React.lazy() 함수를 사용하여 동적으로 로드되는 컴포넌트를 생성할 수 있다. 이 함수는 컴포넌트를 반환하는 함수를 인수로 받는다. 이 함수는 import() 함수를 사용하여 컴포넌트 코드를 로드한다. 아래의 코드가 이와 같다.
import React, {.lazy, Suspense } from 'react';
const MyComponent = lazy(() => import('./MyComponent'));
function App () {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
</div>
)
}
export default App;
위 코드에서 MyComponent는 동적으로 로드되는 컴포넌트이다. React.lazy() 메소드를 사용하여 import('./MyComponent') 코드를 비동기적으로 로드한다. fallback props를 사용하여 로드 중에 보여줄 로딩 상태를 지정할 수도 있다.
이 방법을 규모가 큰 프로젝트를 진행할 때도 사용할 수 있다. 먼저, 라우팅 기반 코드 분할이다. 라우팅 기반 코드 분할 (Routing-based Code Splitting)은 각 라우트별로 코드를 분할하는 방법이다. 이를 통해 사용자가 각 라우트를 방문할 때 필요한 최소한의 코드만 다운로드할 수 있다. 예를 들어 다음과 같은 코드를 사용하여 '/users' 라우트에 필요한 컴포넌트만 로드할 수 있다.
import React, { 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>
);
둘째로 코드 분할 도구를 사용하는 것이다. Webpack, Rollup 등과 같은 코드 분할 도구를 사용하여 코드를 분할할 수도 있다. 이러한 도구를 사용하면 모듈별로 코드를 분할하거나, 엔트리 포인트별로 코드를 분할할 수 있다. 이를 통해 앱의 크기를 줄이고, 초기 로딩 시간을 단축시킬 수 있다.
셋째로, SSR(Server-side Rendering)과 코드 분할이다. SSR과 코드 분할을 함께 사용하면 초기 로딩 시간을 단축시킬 수 있다. 서버 측에서 렌더링하는 과정에서 필요한 컴포넌트만 미리 로드하여 전체 앱의 크기를 줄일 수 있다. 이를 통해 초기 로딩 시간을 단축하고 사용자 경험을 향상할 수 있다.
물론 코드 분할에도 단점이 있다. 코드분할은 복잡성을 증가시킬 수 있다. 비동기 로딩이 포함되기 때문에 코드의 흐름을 이해하기 더 어려워진다.
또 코드 분할을 수행하면 사용자가 앱을 처음 로드할 때 더 많은 HTTP 요청이 발생할 수도 있다. 그러나 이 것은 적절한 캐싱 전략을 사용하면 해결할 수 있는데, 여러 캐싱 전략 중 좀 전에 보았던 라우팅 기반 코드 분할이 코드 스플리팅과 라우팅의 결합으로 해결하는 방식이라 볼 수 있다. 라우팅 경로가 변경될 때마다 새로운 코드 청크를 가져오는데, 이를 최적화하기 위해 라우팅 경로와 코드 청크를 매핑하는 방식으로 코드 스플리팅과 라우팅을 결합하는 것이다. 이를 통해 라우팅 경로에 따라 미리 로드할 청크를 결정하고 불필요한 HTTP 요청을 줄일 수 있다.
마지막으로 코드 분할 전략을 사용할 때의 주의점이다. 먼저 크기를 작게 유지해야 한다. 코드 분할을 하더라도 각 청크의 크기가 너무 커지면 전체 앱 성능에 부정적인 영향을 미칠 수 있다. 따라서 청크 크기를 작게 유지하려고 노력할 필요가 있다. 둘째로, 컴포넌트 분할이 필요한지 확인하는 것이다. 코드 분할은 모든 컴포넌트에 적용되는 것이 아니라 컴포넌트의 크기와 사용빈도에 따라 필요한 경우에만 수행해야 한다. 일반적으로 대형 컴포넌트에만 코드 분할을 적용하는 것이 좋다. 마지막으로 코드 분할 후에도 모듈 의존성을 유지해야 한다. 코드 분할을 수행할 때 각 모듈이 서로에게 의존성을 유지해야 한다. 이를 위해서 Webpack은 코드 분할 후에도 모듈 의존성을 유지하도록 지원한다. 이러한 설정이 적절하게 구성되어야 한다.
'React' 카테고리의 다른 글
useEffectEvent (0) | 2023.03.20 |
---|---|
Context (0) | 2023.03.16 |
React가 추천하는 form 상태관리 툴 formik (0) | 2023.03.14 |
Yup으로 유효성 검사하기 (0) | 2023.03.14 |
useEffect의 mount와 unmount (0) | 2023.03.09 |