이전에 WiniLogis 프로젝트의 성능 최적화를 진행하면서 코드 스플리팅을 적용한 경험이 있습니다.
기존에는 SPA 특성상 메인 페이지에 접속하면 모든 페이지의 리소스가 하나의 JS 파일로 번들링되어 로드되고 있었습니다.
이로 인해 초기 로딩 시 불필요한 코드까지 함께 로드되는 문제가 있었습니다.
이를 개선하기 위해 React의 Lazy와 Suspense를 활용하여 도메인 단위로 파일을 분리하고, 필요한 시점에만 리소스를 불러오도록 구조를 변경했습니다.
그 결과 메인 페이지에서 로드되는 코드 양을 281,862줄 → 63,303줄로 줄이며 성능을 개선할 수 있었습니다.
또한 이외에도 이미지 리소스 최적화, i18n 기반 다국어 처리 개선 등을 함께 진행하며 전반적인 성능 점수와 접근성 점수도 개선할 수 있었습니다.
문제 상황

코드 스플리팅 적용 이후, 한 가지 문제가 발생했습니다.
페이지 이동 시마다 화면이 순간적으로 사라지며 흰 화면이 노출되는 현상이 있었습니다.
또한 SPA임에도 불구하고 페이지 전환이 자연스럽지 않고 뚝뚝 끊기는 느낌을 받게 되었습니다.
원인
App 최상단에서 Suspense를 다음과 같이 사용하고 있었습니다.
App.tsx
/* Lazy Loading 적용 */
const Main = lazy(() => import('./components/main'));
export default function App() {
return (
<Suspense fallback={<></>}>
<Routes>
<Route path='/' element={<Main />} />
...
</Routes>
</Suspense>
);
}
fallback을 비워둔 이유는 다음과 같았습니다.
- 페이지별 API 로딩은 각 페이지에서 개별적으로 처리
- 페이지 전환 시 글로벌 로딩 UI는 UX적으로 적절하지 않다고 판단
하지만 이 구조로 인해 lazy 컴포넌트가 로딩되는 순간 Suspense가 fallback을 렌더링하게 되고, 결과적으로 화면이 비워지면서 흰 화면이 노출되는 문제가 발생했습니다.
고민한 점
이 문제를 해결하기 위해 몇 가지 방향을 고민했습니다.
하지만 이 방식은 각 컴포넌트마다 Suspense와 fallback 처리를 별도로 해줘야 하고, 그에 따라 관리 포인트가 늘어나면서 코드 복잡도가 증가할 수 있다고 판단했습니다.
또한 React의 Suspense 자체를 걷어내고, 코드 스플리팅 없이 일반적인 SPA 구조로 사용하는 방안도 함께 고민했습니다.
하지만 현재 프로젝트의 규모가 큰 상황에서 청킹을 제거할 경우 100KB 이상 크기의 번들 파일이 다수 발생하게 되고, 이는 초기 로딩 성능에 부정적인 영향을 다시 줄 수 있다고 판단했습니다.
결과적으로 코드 스플리팅 구조는 유지하면서 UX 문제를 해결할 수 있는 방향 을 찾는 것이 필요했습니다.
해결 과정
이후 해결 방법을 찾던 중, react-router-dom의 v7_startTransition 옵션을 통해 문제를 해결할 수 있었습니다.
index.tsx
...
root.render(
<>
<BrowserRouter future={{ v7_startTransition: true }}>
<App />
</BrowserRouter>
</>
);
해당 옵션을 활성화하면 라우트 전환이 내부적으로 React의 startTransition으로 감싸지게 됩니다.
startTransition은 상태 업데이트를 transition으로 처리하여, 작업이 진행되는 동안 기존 UI를 유지할 수 있도록 도와주는 기능입니다.
기존에는 페이지 전환 시 lazy 컴포넌트가 로딩되면서 Suspense에 의해 fallback UI가 즉시 렌더링되었고, 이로 인해 흰 화면이 노출되었습니다.
하지만 이 옵션을 적용한 이후에는 페이지가 suspend되더라도 transition 상태로 처리되기 때문에 기존 화면이 유지된 채 로딩이 진행됩니다.
그 결과 lazy 컴포넌트 로딩 중에도 fallback(<></>)이 즉시 노출되지 않고 기존 화면이 유지되며, 로딩이 완료된 시점에 자연스럽게 다음 화면으로 전환되어 사용자 입장에서는 끊김 없이 페이지가 전환되는 것처럼 느껴지게 되었고, 페이지 이동 시 발생하던 흰 화면 문제를 해결할 수 있었습니다.

느낀 점
처음에는 코드 스플리팅 구조나 Suspense 사용 방식의 문제라고 생각해, 청킹 단위를 더 세분화하거나 구조를 변경하는 방향을 고민했습니다.
하지만 큰 구조를 변경하지 않고도 옵션 한 줄로 문제를 해결할 수 있었고, 이번 경험을 통해 또 하나를 배울 수 있었습니다.
또한 개발이 끝난 이후, react-router-dom을 v7으로 업데이트하면 v7_startTransition 옵션이 기본적으로 적용되어 별도의 설정 없이도 동일한 효과를 얻을 수 있다는 점을 알게 되었습니다. 이에 따라 react-router-dom을 7버전 이상으로 업데이트했습니다.
'지식 정리 📝' 카테고리의 다른 글
| Android WebView 디버깅: chrome://inspect Pending authentication 오류 해결 (0) | 2026.03.12 |
|---|---|
| Frontend Kit – AI 문답 기능 도입 (Gemma 3 27B) (0) | 2026.02.27 |
| WiniLogis Webpack에서 Vite로 마이그레이션 (2) | 2026.02.10 |
| Keycloakify를 활용한 React 기반 Keycloak 테마 개발 및 다국어 처리 (2) | 2026.02.09 |
| Lighthouse 90점대인데 체감은 느린 이유? Vercel Region 최적화로 해결 (2) | 2026.02.03 |