클래식 리액트 생산 버그: 새로운 번들을 배포하면, 사용자는 탭에 구식 HTML을 캐시에 저장하고 있을 수 있으며, 그들은 경로로 이동하면 → React.lazy()은 더 이상 CDN에 존재하지 않는 청크를 가져오려고 시도 → 화면이 비어 보인다.
콘솔의 오류는 다음과 같아 보인다.
동적으로 가져온 모듈을 찾지 못했습니다:
https://cdn.example.com/assets/Page-abc123.js
사용자는 하드-리로드 외에는 도피 방법이 없으며, 대부분은 그 사실을 알지 못합니다.
수정
전역 error 리스너는 청크 로드 오류를 포착하고 캐시-브레스트 매개변수와 함께 페이지를 강제로 리로드합니다.
js
const CHUNK_ERROR_PATTERNS = [
/Loading chunk \d+ failed/i,
/Failed to fetch dynamically imported module/i,
/Loading CSS chunk .* failed/i,
/Importing a module script failed/i,
];
window.addEventListener('error', (e) => {
const msg = e?.message || '';
if (CHUNK_ERROR_PATTERNS.some(rx => rx.test(msg))) {
const url = new URL(window.location.href);
url.searchParams.set('_r', String(Date.now()));
window.location.replace(url.toString());
}
});
Why each part matters:
Multiple patterns: different browsers throw different messages. Safari uses "Importing a module script failed", Chrome uses "Failed to fetch dynamically imported module", older Webpack builds use "Loading chunk N failed".
Case insensitive: some browsers capitalize, some don't.
Cache-bust param: ?_r=<timestamp> forces fresh index.html, which has the new chunk hashes.
replace() not assign(): doesn't add a history entry, so back button works.
Gotchas
This catches unhandledrejection too if the lazy import throws inside a promise. Add a second listener if you see misses.
Don't trigger reload more than once: add a flag to avoid loops on a network outage.
Test it: deploy a new build, open the old tab, navigate. Should auto-recover.
It's 15 lines but saved me a real percentage of users abandoning the app post-deploy.











