Architecture
bf-cache 때문에 뒤로가기가 깨질 때
bf-cache란?
bf-cache는 back/forward cache의 줄임말입니다. 브라우저가 뒤로가기나 앞으로가기를 빠르게 처리하기 위해, 이전 페이지의 DOM과 JavaScript heap까지 메모리에 보관해두었다가 그대로 복원하는 기능입니다.
일반적인 HTTP cache가 네트워크 응답을 캐싱한다면, bf-cache는 페이지 상태 자체를 스냅샷처럼 보관합니다. 그래서 복원되면 load가 다시 실행되는 것이 아니라 멈춰 있던 페이지가 이어서 살아납니다. 사용자 입장에서는 거의 즉시 이동되는 것처럼 느껴지기 때문에 성능에는 꽤 좋은 기능입니다.
마이크로 프론트엔드에서 만난 문제
그런데 마이크로 프론트엔드를 구현하면서 이 동작 때문에 문제가 생긴 적이 있습니다. 호스트 앱에서 remote entry를 로드하고, 각 앱의 JavaScript를 동적으로 붙이는 구조였는데, 특정 페이지에서 다른 페이지로 이동한 뒤 브라우저 뒤로가기를 하면 JavaScript가 다시 로드되지 않는 현상이 있었습니다.
처음에는 스크립트 로딩 순서나 module federation 설정 문제라고 생각했습니다. 하지만 원인은 페이지가 새로 로드된 것이 아니라 bf-cache에서 복원된 것이었습니다. 브라우저는 이전 화면을 그대로 되살렸고, 제가 기대했던 초기화 로직은 다시 실행되지 않았습니다. 결과적으로 remote script가 다시 붙지 않거나, 이미 붙었다고 판단한 상태와 실제 런타임 상태가 어긋났습니다.
당시 사용했던 우회 방식
당시에는 early script에서 pageshow 이벤트를 최대한 빨리 등록하고, bf-cache 복원 또는 back/forward navigation이 감지되면 즉시 window.location.reload()를 호출하는 방식으로 우회했습니다. 페이지가 복원되는 순간 바로 새로고침되도록 만들어, 결과적으로 뒤로가기 시 다시 초기 로드 흐름을 타게 만든 것입니다.
<Script id="bfcache-guard" strategy="beforeInteractive">
{BF_CACHE_SCRIPT_STRING}
</Script>
export const BF_CACHE_SCRIPT_STRING = `
window.addEventListener('pageshow', function (event) {
var nav = performance.getEntriesByType('navigation')[0];
var isBackForward = nav && nav.type === 'back_forward';
if (event.persisted || isBackForward) {
window.location.reload();
}
});
`;
문제 해결은 빨랐지만, 지금 생각하면 브라우저가 제공하는 성능 최적화 기능을 우회하는 선택이기도 했습니다. 그래서 이 방식은 상태 복구가 어렵거나, 반드시 새 로드가 필요한 화면에 제한적으로 적용하는 편이 더 적절하다고 생각합니다.
더 나은 접근
더 나은 접근은 뒤로가기마다 무조건 새로고침하기보다 pageshow 이벤트를 기준으로 복원 여부를 감지하고, 복원된 경우 필요한 런타임만 다시 검증하는 방식입니다.
window.addEventListener('pageshow', async (event) => {
if (!event.persisted) return;
await ensureRemoteEntryLoaded();
await ensureSharedScopeReady();
syncRouterState();
refreshRuntimeConfig();
});
마이크로 프론트엔드라면 이 시점에 다음을 확인하는 편이 좋습니다.
- remote entry script가 실제 DOM에 존재하는지
- shared dependency scope가 정상인지
- host와 remote의 router 상태가 어긋나지 않았는지
- 인증, feature flag, config처럼 오래되면 안 되는 값이 갱신되었는지
정말 민감한 페이지라면 복원 직후 새로고침하는 선택도 가능하지만, 기본값으로 삼기에는 아깝습니다. 특히 대부분의 페이지에서는 bf-cache 복원이 사용자 경험에 유리합니다. 그래서 지금 다시 구현한다면 먼저
pageshow와event.persisted로 복원 케이스를 처리하고, 그래도 상태 복구가 어렵거나 보안상 새 로드가 필요한 화면에만 제한적으로 새로고침 우회를 적용할 것 같습니다.
정리
정리하면 bf-cache는 버그가 아니라 브라우저의 최적화입니다. 문제는 우리가 페이지 진입을 항상 “새 로드”라고 가정할 때 생깁니다. 뒤로가기는 새로고침이 아닐 수 있고, 특히 마이크로 프론트엔드처럼 런타임 조립이 많은 구조에서는 이 차이를 의식해야 합니다.