안녕하세요! 웹 YB 황인영입니다.
lazy loading에 대해 아시는 분들이 많겠지만.. 제가 잘 몰라서 이번에 공부하면서 개념에 대해 정리해보고자 합니다.
React.lazy()
React.lazy()는 리액트에서 코드 스플리팅을 쉽게 하기 위한 기능입니다.
리액트는 SPA(Single-Page-Application)이므로, 리액트 앱을 번들링할 때 모든 컴포넌트를 불러와 한 번에 묶습니다.
하지만 React.lazy를 사용하면 필요할 때만 로드하여 초기 로딩 속도를 줄이고 성능을 최적화할 수 있습니다.
즉, 컴포넌트를 필요할 때만 비동기로 로딩할 수 있는 기능을 제공합니다.
const MyComponent = React.lazy(() => import('./MyComponent'));
import()는 동적 import로, 해당 모듈을 Promise 형태로 비동기 로드합니다.
이때, React.lazy()로 로드한 컴포넌트는 반드시 <Suspense>로 감싸야합니다.
왜냐하면 Suspense는 비동기로 로딩되는 컴포넌트를 처리할 수 있는 메커니즘을 가졌기 때문입니다.
이를 이해하기 위해 Suspense가 프로미스 기반으로 작동하는 원리를 조금 더 뜯어봅시다..
Suspense
import React from "react";
export interface SuspenseProps {
fallback: React.ReactNode;
}
interface SuspenseState {
pending: boolean;
error?: any;
}
function isPromise(i: any): i is Promise<any> {
return i && typeof i.then === "function";
}
export default class Suspense extends React.Component<
SuspenseProps,
SuspenseState
> {
public state: SuspenseState = {
pending: false
};
public componentDidCatch(catchedPromise: any) {
if (isPromise(catchedPromise)) {
this.setState({ pending: true });
catchedPromise
.then(() => {
this.setState({ pending: false });
})
.catch((err) => {
this.setState({ error: err || new Error("Suspense Error") });
});
} else {
throw catchedPromise;
}
}
public componentDidUpdate() {
if (this.state.pending && this.state.error) {
throw this.state.error;
}
}
public render() {
return this.state.pending ? this.props.fallback : this.props.children;
}
}
이는 <Suspense> 컴포넌트를 직접 구현한 예제입니다.
컴포넌트가 처음 렌더링되면, 내부 상태(pending, error)는 기본값으로 초기화되어 있고, 자식 컴포넌트가 정상적으로 렌더링됩니다.
하지만 만약 자식 컴포넌트 중 하나가 렌더링 도중 Promise를 throw하면 React는 해당 Promise를 componentDidCatch()로 전달합니다.
componentDidCatch()에서 전달받은 값이 실제로 Promise인지 검사한 후, 맞다면 로딩 중 상태인 pending: true로 설정하고, 그 promise가 끝날 때까지 기다린 후, 성공하면 pending: false로 되돌립니다. 실패하면 error 상태에 에러를 저장합니다.
render 함수에서는 pending === true이면 fallback UI (props.fallback)를 보여줍니다.
컴포넌트에서 이 fallback props를 통해 로딩 UI에 대한 처리를 따로 가능하게 합니다.
만약 로딩이 끝났는데 실패했다면, componentDidUpdate()가 감지해서 저장된 error를 다시 throw합니다. 그럼 상위 컴포넌트가 이 에러를 처리할 수 있게 됩니다. 즉,,, Suspense는 컴포넌트의 에러, 로딩 등의 상태를 감지하고, 잠깐 기다리게 하면서 특정 UI를 띄우는 처리를 하고, 상위 컴포넌트에 알리는 역할을 수행합니다.
따라서 이러한 이유로 React.lazy()로 로드한 컴포넌트를 무조건 Suspense로 감싸야 합니다.
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, lazy를 사용한 간단한 예제입니다.
lazy loading을 사용하면 필요할 때마다 섹션의 컴포넌트와 라이브러리를 로드함으로써 번들링 사이즈를 줄여 초기 로드 속도를 크게 향상시킬 수 있다고 합니다. 프로젝트가 넘 커진다면 lazy loading 사용을 고려해보는 게 좋을 것 같아요! 🐢
'4주차' 카테고리의 다른 글
| Tailwind CSS 버전 정리 (0) | 2025.05.13 |
|---|---|
| Tanstack Query (0) | 2025.05.13 |
| 챗지*티야 빨리 글 좀 써줘!!! (3) | 2025.05.13 |
| useEffect vs useLayoutEffect (0) | 2025.05.13 |
| 변성 가족 알아보기 (공변성, 반공변성, 이변성, 불변성) (0) | 2025.05.13 |