728x90
반응형
스플리팅 등장 배경
리액트 애플리케이션의 경우 빌드를 통해서 배포를 한다. 이 과정에서 파일 크기를 가능하면 최소화 하는 것이 좋다.
왜냐하면 파일 크기가 성능을 결정하고 결과적으로 사용자 경험(UX)에까지 영향을 미치기 때문이다.
또한, 브라우저에서 JSX나 최신 자바스크립트 문법 등이 문제없이 잘 실행될 수 있도록 트랜스파일링하는 작업도 필요하다.
일반적으로 이러한 작업은 빌드 도구인 Webpack, Parcel, Vite 등이 담당한다.
Webpack, Parcel과 같은 경우에는 모든 자바스크립트 파일을 하나의 파일로 합치고, CSS 역시 하나의 파일로 합친다.
하나의 파일로 모든 자바스크립트의 파일로 묶어서 빌드하면 파일의 크기가 매우 크고, 일부만 수정해도 다시 모든 자바스크립트 코드들을 새로 빌드해야 하는 비효율성을 가진다.
Vite는 수정이 되는 부분만 교체를 통해 빌드를 하기 때문에 효율적이다.
코드 스플리팅
코드 스플리팅 : 코드(파일)을 분리하는 작업
❗️더 나은 사용자 경험을 위해 코드를 비동기적으로 로딩하는 방법
예를 들어, 페이지가 /main, /about, /post 이렇게 세 가지 페이지로 이루어진 SPA(Single Page Application)을 개발한다고 할 때, /main으로 들어가는 동안 /about이나 /post 페이지 정보는 사용자에게 필요하지 않을 확률이 높다.
이러한 파일들을 분리하여 지금 사용자에게 필요한 파일만 불러올 수 있다면 로딩도 빠르게 이루어지고 트래픽도 줄어 사용자 경험이 좋아질 수 있다.
⭐️ 지금 당장 필요한 코드가 아니면 따로 분리시켜서, 나중에 필요할 때 불러와서 사용할 수 있다.
리액트에서 코드 스플리팅
React.lazy
⭐️ 컴포넌트를 렌더링하는 시점에 비동기적으로 로딩할 수 있게 해주는 유틸 함수이다.
Suspense
⭐️ 리액트 내장 컴포넌트로 코드 스플리팅 된 컴포넌트를 로딩하고, 로딩이 끝나지 않았을 때 보여줄 UI를 설정할 수 있다.
fallback 이라는 props를 통해 로딩 중에 보여줄 JSX 문법을 지정할 수 있다.
React.lazy + Suspense
import React, { Suspense } from 'react';
const SomeComponent = React.lazy(() => import('./SomeComponent'));
const myComponent = {
return (
<Suspense fallback={<div>Loading...</div>}> // 로딩 중에 보여줄 UI
<SomeComponent />
</Suspense>
)
}
React.lazy를 사용하지 않고 스플리팅을 하는 방법(레거시 프로젝트)
16.6 버전 이전에 했던 방식으로 동적 import와 state를 활용해 구현 가능하다.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [Component, setComponent] = useState(null);
useEffect(() => {
const importComponent = async () => {
const { default: LazyComponent } = await import('./SomeComponent'); // 비동기 로딩
setComponent(LazyComponent);
};
importComponent();
}, []);
return (
<div>
{Component ? <Component /> : 'Loading...'} {/*로딩이 되는 동안 'Loading' 출력 */}
</div>
);
}
코드 분할을 결정하는 요소
1. 라우트 기반 분할(Route Level)
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home')); // 비동기 로딩 세팅
const About = lazy(() => import('./routes/About')); // 비동기 로딩 세팅
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch> {/*Switch 태그: 하나의 URL에 대해서만 매칭되는 Route를 선택하는 역할*/}
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
⭐️ 라우트마다 다른 컴포넌트로 관리를 하고 있을 겨우, 각 라우트를 동적 import를 통해 분리된 빌드 파일로 관리할 수 있다.
유저가 다른 페이지로 넘어갈 때만 그 페이지를 비동기적으로 로딩할 수 있다.
2. 컴포넌트 기반 분할(Component Level)
// Notify.tsx
const Notify = () => {
window.alert('notify!');
};
export default Notify;
// App.tsx
import React, {useState} from 'react';
import {TextField, Box, Button} from '@mui/material';
const App = () => {
const handleNotify = () => {
import('./Notify').then(({default: Notify}) => {
Notify();
});
};
return (
<Button variant='contained' onClick={handleNotify}>
추가
</Button>
)
}
export default App;
⭐️ 페이지 안에 있지만 보이지 않는 컴포넌트가 존재할 수 있다.
예를 들어 유저가 이메일 페이지에서 새로운 메일을 작성하고자 할 때, 추가하기 버튼을 눌러 알림이 뜨게 된다면 그 알림을 import()로 스플리팅해서 관리할 수 있다.
⭐️ 추가 Button을 누르면 example.chunk.js 라는 파일을 불러오게 된다.
import 함수를 사용하면 웹팩이 알아서 코드를 분리해서 저장해주고, import가 호출할 때 불러와서 사용할 수 있게 해준다.
3. 하나의 페이지를 스플리팅
⭐️ 페이지 하나의 코드가 긴 경우, 그 페이지에 들어갈 때 당장 보이는 부분을 나머지와 분리하고 그 뒷부분을 다른 컴포넌트로 만들어 스플리팅할 수 있다.
// Home.js
import React, { lazy, Suspense } from 'react';
const AboutSection = lazy(() => import('./AboutSection'));
const Home = () => {
return (
<div>
{/* 페이지 상단 부분 */}
<h1>Home Page</h1>
<p>이것은 홈 페이지의 상단 부분입니다.</p>
<Suspense fallback={<div>Loading...</div>}>
<AboutSection />
</Suspense>
</div>
);
};
export default Home;
// AboutSection.js
import React from 'react';
const AboutSection = () => {
return (
<div>
{/* 페이지 하단 부분 */}
<h2>About Us</h2>
<p>저희 회사는...</p>
</div>
);
};
export default AboutSection;
Webpack : Entry Point
Entry Point는 웹팩이 앱에서 번들링하려는 모듈의 진입파일이다.
리액트 앱이 여러 엔트리 포인트를 설정한다면 각각의 엔트리 포인트마다 코드 스플리팅이 가능하다.
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
another: './src/another-module.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
entry 프로퍼티를 작성하면 웹팩에서 자동으로 'index'와 'another'를 다른 chunk로 관리를 해서 로딩한다.
웹팩은 둘 간의 의존성(dependency)도 분리를 해서 관리를 하는데, 만약 같은 의존성을 여러 entry point에서 가진다면, 중복된 로딩이 많아져서 성능 저하를 일으킬 수 있다.
⭐️ 웹팩의 중복 의존성 관리 방법
웹팩은 optimization.splitChunks 옵션을 통해 중복된 의존성을 관리합니다. 이 옵션을 사용하면 다음과 같은 방식으로 중복 의존성을 처리할 수 있습니다.
chunks 옵션: 어떤 청크에 대해 중복된 의존성을 분리할지 설정합니다.
minChunks 옵션: 최소 몇 개의 청크에서 공유되는 의존성을 별도의 청크로 분리할지 설정합니다.
maxInitialRequests 옵션: 초기 로딩 시 최대 몇 개의 청크를 로딩할지 설정합니다.
cacheGroups 옵션: 커스텀 캐시 그룹을 생성하여 더욱 세밀하게 중복 의존성을 관리할 수 있습니다.
Reference
[React] 코드 스플리팅 (Code Splitting)
728x90
반응형
'프론트엔드 > React' 카테고리의 다른 글
[Vite] Vite의 역할 (1) | 2024.11.05 |
---|---|
[React] 프로젝트 세팅 (0) | 2024.11.05 |