🚀 동적 import와 Suspense의 고급 활용법
React의 코드 스플리팅을 위한 가장 대표적인 방법은 React.lazy()
와 Suspense
조합이에요.
이전 글에서 살펴본 기본 구조는 다음과 같죠.
const MyComponent = React.lazy(() => import('./MyComponent'));
<Suspense fallback={<Loading />}>
<MyComponent />
</Suspense>
이 방식은 아주 강력하고 간단하지만, 실제 프로젝트에서 더 유연하게 사용하기 위해서는
동적 import와 Suspense를 다양한 방식으로 활용할 줄 알아야 합니다.
이번 글에서는 React.lazy()
의 한계를 넘어서,
비동기 상황 대응, 조건부 렌더링, 중첩 Suspense, 모듈 레벨 import, fallback 최적화 등
실전에서 유용한 고급 패턴들을 소개할게요.
✅ 1. 조건부 동적 import: 사용자가 클릭할 때만 로딩
모든 컴포넌트를 앱 시작과 동시에 lazy-load 할 필요는 없어요.
오히려 특정 사용자 행동이 발생할 때만 컴포넌트를 불러오는 것이 더 효과적일 수 있습니다.
예시: 버튼 클릭 시 모달을 불러오는 경우
import { useState, lazy, Suspense } from "react";
const LazyModal = lazy(() => import("./Modal"));
function Page() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>모달 열기</button>
{showModal && (
<Suspense fallback={<div>모달 로딩 중...</div>}>
<LazyModal onClose={() => setShowModal(false)} />
</Suspense>
)}
</div>
);
}
- ✅ 초기에 모달 컴포넌트 코드가 로드되지 않음
- ✅ 사용자가 클릭했을 때만 import 실행됨
- ✅ 성능과 UX 모두 잡을 수 있음
✅ 2. 중첩 Suspense: 컴포넌트별 로딩 상태 관리
여러 개의 lazy 컴포넌트를 하나의 Suspense로 감싸면,
한 컴포넌트가 로드되지 않아도 전체 fallback이 뜨게 됩니다.
→ 컴포넌트별로 Suspense를 중첩해서 처리하면 좋아요.
<Suspense fallback={<div>헤더 로딩 중...</div>}>
<Header />
</Suspense>
<Suspense fallback={<div>본문 로딩 중...</div>}>
<Main />
</Suspense>
- ✅ 전역에 하나만 사용하는 것보다 UX에 유리
- ✅ 로딩 중에도 다른 영역 먼저 렌더링 가능
✅ 3. named export 모듈도 lazy-load 하기
React.lazy()
는 기본적으로 default export만 지원해요.
하지만 일부 라이브러리나 구조는 named export만 사용하죠.
→ 이럴 땐 다음처럼 default wrapping을 해주면 됩니다.
const LazyChart = lazy(() =>
import("./Chart").then((module) => ({ default: module.Chart }))
);
- ✅ module.Chart는 named export
- ✅
default: module.Chart
형태로 감싸야 React.lazy가 인식 가능
✅ 4. 비동기 로직 안에서 Suspense 적용
컴포넌트를 lazy-load 하는 시점을 상태나 조건에 따라 제어하고 싶을 수도 있죠.
function DynamicComponentLoader({ type }) {
const Component = lazy(() =>
import(`./components/${type}`).catch(() =>
import("./components/NotFound")
)
);
return (
<Suspense fallback={<p>컴포넌트 로딩 중...</p>}>
<Component />
</Suspense>
);
}
- ✅ 동적 경로 지원
- ✅ 예외 발생 시 fallback import 가능
✅ 5. fallback UI를 더 정교하게 구성하기
단순한 “로딩 중...” 대신 스켈레톤 UI / 애니메이션 / 에러 fallback 등을 활용하면 UX가 향상됩니다.
<Suspense fallback={isMobile ? <MobileSkeleton /> : <DesktopSkeleton />}>
<Dashboard />
</Suspense>
- ✅ 사용자 환경에 따라 fallback UI 다양화
- ✅ 로딩 시 불쾌감 최소화
✅ 6. React.lazy 없이 직접 import()로 렌더링 제어하기
React.lazy()
대신 import()
를 직접 사용해 컴포넌트를 수동으로 로딩할 수도 있어요.
function DynamicImportDemo() {
const [Comp, setComp] = useState(null);
useEffect(() => {
import("./HeavyComponent").then((mod) => {
setComp(() => mod.default);
});
}, []);
if (!Comp) return <p>로딩 중...</p>;
return <Comp />;
}
- ✅ Suspense 없이 사용할 수 있음
- ✅ 커스텀 로직/오류 핸들링/SSR 분리에 유리
✅ 7. React 18의 가능성: 서버 컴포넌트 + 스트리밍
React 18부터는 Suspense
가 SSR에서도 작동 가능해졌습니다.
Next.js 13+에서는 서버 컴포넌트와 함께 스트리밍 렌더링을 지원해요.
<Suspense fallback={<p>댓글 로딩 중...</p>}>
<CommentList />
</Suspense>
- ✅ 서버에서 컴포넌트를 나눠서 보내고 클라이언트에서 조립
- ✅ SSR + CSR 병합 UX 구성 가능
- ⚠️ Next.js 기반에서만 가능
✅ 마무리 정리
React의 Suspense와 코드 스플리팅은 단순한 로딩 처리 도구를 넘어서
성능 + 사용자 경험 + 유지보수성까지 개선할 수 있는 강력한 전략입니다.
📌 오늘 배운 고급 활용법 요약
- ✔ 조건부 import: 유저 액션 이후 로드
- ✔ 중첩 Suspense: 컴포넌트별 로딩 처리
- ✔ named export도 lazy-load 가능
- ✔ 동적 import 경로/예외 대응
- ✔ 다양한 fallback 구성으로 UX 강화
- ✔ React.lazy 없이 import()로 직접 제어도 가능
- ✔ React 18 이후 SSR + Suspense 연계 가능
Suspense는 단순히 ‘로딩 중...’을 보여주는 컴포넌트가 아닙니다.
React 앱 구조 자체를 최적화하는 도구예요! 😎
다음 글에서는 React 앱에서 로딩 상태 관리와 사용자 경험을 더욱 부드럽게 만드는 팁도 함께 다뤄볼게요!
'일상이 개발' 카테고리의 다른 글
React 버튼 클릭 피드백 제대로 만들기: 딜레이 없는 자연스러운 UX 설계 (0) | 2025.04.06 |
---|---|
React 로딩 상태 관리 가이드: 사용자 경험을 부드럽게 만드는 실전 팁 (0) | 2025.04.06 |
React 앱 로딩 속도 향상 전략: Suspense + 코드 스플리팅 실전 정리 (0) | 2025.04.05 |
React 앱 성능 측정과 렌더링 최적화 전략: DevTools부터 useMemo까지 정복하기 (0) | 2025.04.05 |
React DevTools 완전 정복: 리렌더링 감지와 성능 최적화 방법 가이드 (0) | 2025.04.05 |