뒤로-앞으로 캐시

뒤로-앞으로 캐시 (또는 bfcache)는 즉각적인 뒤로-앞으로 탐색을 가능하게 하는 브라우저 최적화입니다. 특히 네트워크나 기기 속도가 느린 사용자의 경우 탐색 환경이 크게 개선됩니다.

웹 개발자는 사용자가 이점을 누릴 수 있도록 bfcache에 맞게 페이지를 최적화하는 방법을 이해하는 것이 중요합니다.

브라우저 호환성

모든 주요 브라우저에는 Chrome 버전 96 이후, Firefox, Safari를 비롯한 bfcache가 포함되어 있습니다.

bfcache 기본사항

뒤로/앞으로 캐시(bfcache)를 사용하면 사용자가 탐색할 때 페이지를 소멸하는 대신 소멸을 연기하고 JS 실행을 일시중지합니다. 사용자가 곧 다시 탐색하면 페이지가 다시 표시되고 JS 실행 일시중지가 해제됩니다. 따라서 사용자는 거의 즉시 페이지를 탐색할 수 있습니다.

웹사이트를 방문하고 링크를 클릭하여 다른 페이지로 이동했으나 원하는 것이 아님을 깨닫고 뒤로 버튼을 클릭한 횟수는 몇 번입니까? 이때 bfcache로 인해 이전 페이지의 로드 속도가 크게 달라질 수 있습니다.

bfcache가 사용 설정되지 않은 이전 페이지를 로드하기 위해 새 요청이 시작됩니다. 해당 페이지가 반복 방문에 얼마나 잘 최적화되어 있는지에 따라 브라우저에서 방금 다운로드한 리소스의 일부 또는 전부를 다시 다운로드하고 다시 파싱한 다음 다시 실행해야 할 수 있습니다.
bfcache 사용 이전 페이지는 본질적으로 즉시 로드됩니다. 네트워크로 이동하지 않고도 전체 페이지를 메모리에서 복원할 수 있기 때문입니다.

bfcache가 탐색 속도를 높이는 방법을 알아보려면 bfcache가 작동하는 모습을 보여주는 이 동영상을 확인하세요.

bfcache를 사용하면 뒤로 및 앞으로 탐색 중에 페이지가 훨씬 더 빠르게 로드됩니다.

동영상에서 bfcache를 사용한 예는 bfcache를 사용하지 않은 예시보다 상당히 빠릅니다.

bfcache는 탐색 속도를 높일 뿐만 아니라 데이터 사용량도 줄여줍니다. 리소스를 다시 다운로드할 필요가 없기 때문입니다.

Chrome 사용 데이터에 따르면 데스크톱에서는 탐색 10번 중 1번, 모바일에서는 탐색 5번 중 1번이 뒤로 또는 앞으로 이동하는 것으로 나타났습니다. bfcache를 사용 설정하면 브라우저에서 매일 수십억 개의 웹페이지를 로드하는 데 드는 데이터 전송과 시간이 사라집니다.

'캐시'의 작동 방식

bfcache에서 사용하는 '캐시'는 반복 탐색 속도를 높이는 데 자체 역할을 하는 HTTP 캐시와 다릅니다. bfcache는 JavaScript 힙을 포함하여 메모리에 있는 전체 페이지의 스냅샷인 반면 HTTP 캐시에는 이전에 이루어진 요청에 대한 응답만 포함됩니다. 페이지를 로드하는 데 필요한 모든 요청이 HTTP 캐시에서 처리되는 경우는 매우 드물기 때문에 bfcache 복원을 사용한 반복 방문은 가장 최적화된 비bfcache 탐색보다 항상 빠릅니다.

나중에 다시 사용 설정할 수 있도록 페이지를 동결하면 진행 중인 코드를 가장 잘 보존하는 방법과 관련하여 약간의 복잡성이 발생합니다. 예를 들어 페이지가 bfcache에 있는 동안 제한 시간에 도달한 setTimeout() 호출을 어떻게 처리하나요?

브라우저는 JavaScript 작업 큐의 거의 모든 대기 중인 작업을 포함하여 bfcache의 페이지에 대한 대기 중인 타이머 또는 해결되지 않은 약속을 일시중지하고 페이지가 bfcache에서 복원되면 작업 처리를 재개합니다.

시간 초과 및 약속과 같은 경우에는 위험이 상당히 낮지만 혼란스럽거나 예기치 않은 동작으로 이어질 수도 있습니다. 예를 들어 브라우저가 IndexedDB 거래의 일부로 필요한 작업을 일시중지하면 동일한 IndexedDB 데이터베이스에 여러 탭에서 동시에 액세스할 수 있으므로 동일한 출처의 다른 열려 있는 탭에 영향을 줄 수 있습니다. 따라서 브라우저는 일반적으로 IndexedDB 트랜잭션 중이나 다른 페이지에 영향을 줄 수 있는 API를 사용하는 동안 페이지를 캐시하려고 시도하지 않습니다.

다양한 API 사용이 페이지의 bfcache 사용 자격요건에 미치는 영향에 관한 자세한 내용은 bfcache에 맞게 페이지 최적화를 참고하세요.

bfcache 및 iframe

페이지에 삽입된 iframe이 포함된 경우 iframe 자체는 bfcache를 사용할 수 없습니다. 예를 들어 iframe 내에서 다른 페이지로 이동한 후 뒤로 이동하면 브라우저는 기본 프레임이 아닌 iframe 내에서 '뒤로' 이동하지만 iframe 내의 뒤로 탐색은 bfcache를 사용하지 않습니다.

삽입된 iframe에서 이를 차단하는 API를 사용하는 경우 기본 프레임이 bfcache를 사용하지 못하도록 차단할 수도 있습니다. 이를 방지하려면 기본 프레임에 설정된 권한 정책을 사용하거나 sandbox 속성을 사용하면 됩니다.

bfcache 및 단일 페이지 앱(SPA)

bfcache는 브라우저에서 관리하는 탐색과 함께 작동하므로 단일 페이지 앱(SPA) 내의 '조용히 탐색'에는 작동하지 않습니다. 그러나 bfcache는 앱을 처음부터 다시 완전히 초기화하는 대신 SPA로 돌아갈 때 유용합니다.

bfcache를 관찰하는 API

bfcache는 브라우저에서 자동으로 실행하는 최적화이지만, 개발자가 페이지를 최적화하고 측정항목 또는 성능 측정을 조정할 수 있도록 언제 실행되는지 파악하는 것이 중요합니다.

bfcache를 관찰하는 데 사용되는 기본 이벤트는 대부분의 브라우저에서 지원되는 페이지 전환 이벤트 pageshowpagehide입니다.

최신 페이지 수명 주기 이벤트인 freezeresume는 페이지가 bfcache에 진입하거나 종료될 때뿐만 아니라 CPU 사용량을 최소화하기 위해 백그라운드 탭이 동결되는 경우와 같은 다른 상황에서도 전달됩니다. 이러한 이벤트는 Chromium 기반 브라우저에서만 지원됩니다.

페이지가 bfcache에서 복원되는 시점 관찰

pageshow 이벤트는 페이지가 처음 로드될 때 load 이벤트 직후에 발생하며 언제든 페이지가 bfcache에서 복원될 때 발생합니다. pageshow 이벤트에는 persisted 속성이 있습니다. 이 속성은 페이지가 bfcache에서 복원된 경우 true이고 그렇지 않은 경우 false입니다. persisted 속성을 사용하여 일반 페이지 로드와 bfcache 복원을 구분할 수 있습니다. 예를 들면 다음과 같습니다.

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

Page Lifecycle API를 지원하는 브라우저에서 resume 이벤트는 페이지가 bfcache에서 복원될 때(pageshow 이벤트 직전)와 사용자가 고정된 백그라운드 탭을 다시 방문할 때 발생합니다. 페이지가 정지된 후(bfcache의 페이지 포함) 페이지 상태를 업데이트하려면 resume 이벤트를 사용하면 되지만 사이트의 bfcache 히트율을 측정하려면 pageshow 이벤트를 사용해야 합니다. 경우에 따라 둘 다 사용해야 할 수도 있습니다.

bfcache 측정 권장사항에 대한 자세한 내용은 bfcache가 분석 및 성능 측정에 미치는 영향을 참고하세요.

페이지가 bfcache에 들어가는 시점 관찰

pagehide 이벤트는 페이지가 언로드될 때 또는 브라우저가 페이지를 bfcache에 배치하려고 할 때 발생합니다.

pagehide 이벤트에는 persisted 속성도 있습니다. false이면 페이지가 bfcache에 들어가지 않을 것이라고 확신할 수 있습니다. 하지만 persistedtrue 상태라고 해서 페이지가 캐시된다는 보장은 없습니다. 즉, 브라우저가 페이지를 캐시하려고 시도하지만 캐시할 수 없는 다른 요인이 있을 수 있습니다.

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

마찬가지로 persistedtrue인 경우 freeze 이벤트는 pagehide 이벤트 직후에 실행되지만 이는 브라우저가 페이지를 캐시할 의도만을 의미합니다. 나중에 설명하는 여러 가지 이유로 삭제해야 할 수도 있습니다.

bfcache에 맞게 페이지 최적화

모든 페이지가 bfcache에 저장되는 것은 아니며 페이지가 bfcache에 저장되더라도 무기한 유지되지는 않습니다. 개발자가 캐시 적중률을 극대화하려면 페이지가 bfcache에 적합하거나 적합하지 않게 되는 이유를 이해하는 것이 중요합니다.

다음 섹션에서는 브라우저가 페이지를 최대한 캐시할 수 있도록 하는 권장사항을 간략하게 설명합니다.

unload 이벤트 사용 안함

모든 브라우저에서 bfcache에 맞게 최적화하는 가장 중요한 방법은 unload 이벤트를 사용하지 않는 것입니다. 절대!

unload 이벤트는 bfcache보다 이전이고 인터넷의 많은 페이지가 unload 이벤트가 실행된 후 페이지가 계속 존재하지 않는다는 (합리적인) 가정하에 작동하기 때문에 브라우저에 문제가 됩니다. 이러한 페이지 중 상당수는 사용자가 탐색할 때마다 unload 이벤트가 실행된다는 가정으로 빌드되었기 때문에 문제가 됩니다. 이는 더 이상 사실이 아니며 오래전부터 사실이 아니었습니다.

따라서 브라우저는 딜레마에 직면해 있습니다. 따라서 브라우저는 사용자 환경을 개선할 수는 있지만 페이지가 손상될 위험이 있는 것 중에서 하나를 선택해야 합니다.

데스크톱에서 Chrome과 Firefox는 unload 리스너를 추가하는 경우 페이지가 bfcache를 사용할 수 없도록 선택했습니다. 이는 위험이 적지만 많은 페이지를 제외합니다. Safari는 unload 이벤트 리스너를 사용하여 일부 페이지를 캐시하려고 하지만, 잠재적인 중단을 줄이기 위해 사용자가 탐색을 종료할 때 unload 이벤트를 실행하지 않으므로 이벤트가 매우 불안정해집니다.

모바일에서는 unload 이벤트가 항상 모바일에서 매우 불안정하여 손상 위험이 낮으므로 Chrome과 Safari에서는 unload 이벤트 리스너로 페이지를 캐시하려고 시도합니다. Firefox는 모든 브라우저가 WebKit 렌더링 엔진을 사용해야 하는 iOS를 제외하고 unload를 사용하는 페이지를 bfcache에 사용할 수 없는 것으로 취급하므로 Safari처럼 동작합니다.

unload 이벤트 대신 pagehide 이벤트를 사용하세요. pagehide 이벤트는 unload 이벤트가 발생하는 모든 경우에 발생하며 페이지가 bfcache에 배치될 때도 발생합니다.

실제로 Lighthouse에는 no-unload-listeners 감사가 있습니다. 이 감사는 페이지의 JavaScript(서드 파티 라이브러리의 JavaScript 포함)가 unload 이벤트 리스너를 추가하는 경우 개발자에게 경고합니다.

비신뢰성 및 bfcache의 성능 영향으로 인해 Chrome에서는 unload 이벤트를 지원 중단할 계획입니다.

권한 정책을 사용하여 페이지에서 로드 취소 핸들러가 사용되지 않도록 방지

unload 이벤트 핸들러를 사용하지 않는 사이트는 권한 정책을 사용하여 이벤트 핸들러를 추가하지 않도록 할 수 있습니다.

Permission-Policy: unload=()

또한 서드 파티 또는 확장 프로그램이 언로드 핸들러를 추가하고 사이트에 bfcache를 사용할 수 없도록 하여 사이트 속도를 저하시키는 것을 방지합니다.

조건부로 beforeunload 리스너만 추가

beforeunload 이벤트로 인해 최신 브라우저에서 bfcache에서 bfcache를 사용할 수 있는 것은 아니지만 이전에는 bfcache에서 bfcache를 사용할 수 없게 되었지만 여전히 불안정하므로 꼭 필요한 경우가 아니라면 사용하지 않는 것이 좋습니다.

그러나 unload 이벤트와 달리 beforeunload의 적법한 용도가 있습니다. 예를 들어 사용자가 페이지에서 나가면 저장되지 않은 변경사항이 삭제된다고 사용자에게 경고하려는 경우 이 경우 사용자에게 저장되지 않은 변경사항이 있을 때만 beforeunload 리스너를 추가하고 저장되지 않은 변경사항이 저장된 후 즉시 삭제하는 것이 좋습니다.

금지사항
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
이 코드는 조건부로 beforeunload 리스너를 추가합니다.
권장사항
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
이 코드는 필요할 때만 beforeunload 리스너를 추가하고 필요하지 않을 때 삭제합니다.

Cache-Control: no-store 사용 최소화

Cache-Control: no-store는 웹 서버가 응답에 설정할 수 있는 HTTP 헤더로, 브라우저에 응답을 HTTP 캐시에 저장하지 말라고 지시합니다. 로그인 뒤의 페이지와 같이 민감한 사용자 정보가 포함된 리소스에 사용됩니다.

bfcache는 HTTP 캐시가 아니지만, 이전에는 Cache-Control: no-store가 하위 리소스가 아닌 페이지 리소스 자체에 설정된 경우 브라우저가 페이지를 bfcache에 저장하지 않도록 선택했기 때문에 Cache-Control: no-store를 사용하는 페이지는 bfcache를 사용할 수 없습니다. 개인 정보를 보호하는 방식으로 Chrome의 이 동작을 변경하기 위한 작업이 진행 중입니다.

Cache-Control: no-store는 페이지의 bfcache 사용 자격을 제한하므로 어떠한 종류의 캐싱도 적절하지 않은 민감한 정보가 포함된 페이지에만 설정해야 합니다.

항상 최신 콘텐츠를 게재해야 하며 콘텐츠에 민감한 정보가 포함되지 않은 페이지의 경우 Cache-Control: no-cache 또는 Cache-Control: max-age=0를 사용합니다. 이러한 지시문은 브라우저에 콘텐츠를 게재하기 전에 콘텐츠를 재검증하도록 지시하며 페이지의 bfcache 자격요건에는 영향을 미치지 않습니다.

페이지가 bfcache에서 복원되면 HTTP 캐시가 아닌 메모리에서 복원됩니다. 따라서 Cache-Control: no-cache 또는 Cache-Control: max-age=0와 같은 지시어는 고려되지 않으며 콘텐츠가 사용자에게 표시되기 전에 재검증이 발생하지 않습니다.

그러나 bfcache 복원이 즉시 이루어지고 페이지가 bfcache에 아주 오랫동안 머무르지 않기 때문에 콘텐츠가 최신 상태가 아닐 가능성도 낮기 때문에 사용자 환경이 개선될 가능성이 높습니다. 그러나 콘텐츠가 분 단위로 변경되는 경우 다음 섹션에 설명된 대로 pageshow 이벤트를 사용하여 업데이트를 가져올 수 있습니다.

bfcache 복원 후 비활성 또는 민감한 데이터 업데이트

사이트에서 사용자 상태(특히 민감한 사용자 정보)를 유지하는 경우 페이지가 bfcache에서 복원된 후 해당 데이터를 업데이트하거나 삭제해야 합니다.

예를 들어 사용자가 결제 페이지로 이동한 후 장바구니를 업데이트하는 경우, 오래된 페이지가 bfcache에서 복원되면 뒤로 탐색 시 오래된 정보가 노출될 수 있습니다.

더 심각한 예로는 사용자가 공용 컴퓨터에서 사이트에서 로그아웃한 후 다음 사용자가 뒤로 버튼을 클릭하는 경우를 들 수 있습니다. 이로 인해 사용자가 로그아웃할 때 삭제되었다고 생각한 비공개 데이터가 노출될 수 있습니다.

이러한 상황을 방지하려면 event.persistedtrue이면 pageshow 이벤트 후에 항상 페이지를 업데이트하는 것이 좋습니다.

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

제자리에 있는 콘텐츠를 업데이트하는 것이 이상적이지만, 일부 변경의 경우 강제로 완전히 새로고침해야 할 수 있습니다. 다음 코드는 pageshow 이벤트에 사이트별 쿠키가 있는지 확인하고 쿠키를 찾을 수 없는 경우 새로고침합니다.

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

새로고침은 기록을 유지하므로(앞으로 탐색을 허용하기 위해) 유리하지만 경우에 따라 리디렉션이 더 적절할 수 있습니다.

광고 및 bfcache 복원

bfcache를 사용하지 않고 각 뒤로/앞으로 탐색 시 새로운 광고 세트를 게재하고 싶을 수 있습니다. 그러나 이러한 동작은 실적에 영향을 미칠 뿐만 아니라 광고 참여도를 개선하는 데 도움이 되는지 의문입니다. 사용자가 클릭하려고 돌아가려는 광고를 발견했지만 bfcache에서 복원하는 대신 새로고침하면 클릭할 수 없게 될 수 있습니다. 가정을 하기 전에 이 시나리오를 테스트하는 것이 좋습니다(가급적 A/B 테스트를 사용).

bfcache 복원 시 광고를 새로고침하려는 사이트의 경우 event.persistedtrue일 때 pageshow 이벤트에서 광고만 새로고침하면 페이지 실적에 영향을 주지 않고 새로고침할 수 있습니다. 광고 제공업체에 문의하세요. Google 게시자 태그로 이를 수행하는 방법의 한 가지 예는 다음과 같습니다.

window.opener 참조 피하기

이전 브라우저에서는 rel="noopener"를 지정하지 않고 target=_blank가 있는 링크에서 window.open()를 사용하여 페이지를 열면 열리는 페이지의 창 객체에 대한 참조가 열리는 페이지에 포함되었습니다.

null이 아닌 window.opener 참조가 있는 페이지는 보안 위험이 될 뿐만 아니라 bfcache에 안전하게 배치할 수 없습니다. 액세스하려는 페이지가 손상될 수 있기 때문입니다.

따라서 window.opener 참조를 만들지 않는 것이 가장 좋습니다. 가능한 경우 rel="noopener"를 사용하여 이 작업을 수행할 수 있습니다 (참고: 현재 모든 최신 브라우저에서 기본값임). 사이트에서 창을 열고 window.postMessage()를 통해 제어하거나 창 객체를 직접 참조해야 하는 경우 열린 창이나 opener는 bfcache를 사용할 수 없습니다.

사용자가 나가기 전에 열린 연결 닫기

앞서 언급한 대로 페이지가 bfcache에 보관되면 모든 예약된 JavaScript 작업이 일시중지되고 페이지가 캐시에서 삭제되면 다시 시작됩니다.

이러한 예약된 JavaScript 작업이 DOM API 또는 현재 페이지로만 격리된 다른 API에만 액세스하는 경우 페이지가 사용자에게 표시되지 않는 동안 이러한 작업을 일시중지해도 문제가 발생하지 않습니다.

그러나 이러한 작업이 동일한 출처의 다른 페이지에서도 액세스할 수 있는 API(예: IndexedDB, 웹 잠금, WebSockets)에 연결된 경우 이러한 작업을 일시중지하면 다른 탭의 코드가 실행되지 않을 수 있으므로 문제가 될 수 있습니다.

따라서 일부 브라우저는 다음 시나리오에서 페이지를 bfcache에 저장하려고 시도하지 않습니다.

페이지에서 이러한 API를 사용하는 경우 pagehide 또는 freeze 이벤트 중에 연결을 닫고 관찰자를 삭제하거나 연결 해제하는 것이 좋습니다. 이렇게 하면 브라우저가 열려 있는 다른 탭에 영향을 주지 않고도 페이지를 안전하게 캐시할 수 있습니다.

그런 다음 페이지가 bfcache에서 복원되면 pageshow 또는 resume 이벤트 중에 해당 API를 다시 열거나 다시 연결할 수 있습니다.

다음 예시에서는 pagehide 이벤트 리스너에서 열린 연결을 닫아 IndexedDB를 사용하는 페이지가 bfcache를 사용할 수 있도록 하는 방법을 보여줍니다.

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

페이지를 캐시할 수 있는지 테스트하기

Chrome DevTools를 사용하면 테스트를 통해 페이지가 bfcache에 최적화되어 있는지 확인하고 bfcache 사용을 가로막을 가능성이 있는 문제를 식별할 수 있습니다.

페이지를 테스트하려면 다음 단계를 따르세요.

  1. Chrome에서 페이지로 이동합니다.
  2. DevTools에서 애플리케이션 -> 뒤로-앞으로 캐시로 이동합니다.
  3. 테스트 실행 버튼을 클릭합니다. 그런 다음 DevTools는 페이지에서 나갔다가 다시 돌아와 페이지가 bfcache에서 복원될 수 있는지 확인하려고 합니다.
DevTools의 뒤로-앞으로 캐시 패널
DevTools의 뒤로-앞으로 캐시 패널

테스트가 성공하면 패널에 '뒤로-앞으로 캐시에서 복원됨'이라고 표시됩니다.

DevTools에서 페이지가 bfcache에서 복원되었다고 보고함
복원된 페이지

실패하면 패널에 그 이유가 표시됩니다. 개발자가 해결할 수 있는 문제인 경우 패널에 조치 가능이라고 표시됩니다.

DevTools에서 bfcache에서 페이지를 복원하지 못했다고 보고함
조치 가능한 결과가 있는 실패한 bfcache 테스트입니다.

이 예에서는 unload 이벤트 리스너를 사용하면 페이지가 bfcache에 적합하지 않게 됩니다. unload에서 pagehide를 사용하도록 전환하여 이 문제를 해결할 수 있습니다.

권장사항
window.addEventListener('pagehide', ...);
금지사항
window.addEventListener('unload', ...);

Lighthouse 10.0에는 또한 비슷한 테스트를 실행하는 bfcache 감사가 추가되었습니다. 자세한 내용은 bfcache 감사 문서를 참고하세요.

bfcache가 분석 및 성능 측정에 미치는 영향

애널리틱스 도구를 사용하여 사이트 방문을 측정하는 경우 Chrome에서 더 많은 사용자에게 bfcache를 사용 설정함에 따라 보고되는 총 페이지 조회수가 감소할 수 있습니다.

사실, 많은 인기 분석 라이브러리가 bfcache 복원을 새 페이지 조회로 측정하지 않으므로 bfcache를 구현하는 다른 브라우저의 페이지 조회가 이미 적게 보고되고 있을 수 있습니다.

페이지 조회수에 bfcache 복원을 포함하려면 pageshow 이벤트의 리스너를 설정하고 persisted 속성을 확인합니다.

다음 예에서는 Google 애널리틱스를 사용하여 이 작업을 수행하는 방법을 보여줍니다. 다른 분석 도구에서도 비슷한 로직을 사용할 수 있습니다.

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

bfcache 적중률 측정

bfcache를 사용하지 않는 페이지를 식별하는 데 도움이 되도록 bfcache가 사용되었는지 측정하는 것도 좋습니다. 페이지 로드의 탐색 유형을 측정하면 됩니다.

// Send a navigation_type when the page is first loaded.
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

back_forward 탐색 및 back_forward_cache 탐색의 개수를 사용하여 bfcache 적중률을 계산합니다.

뒤로/앞으로 탐색 시 bfcache가 사용되지 않는 경우는 사이트 소유자가 제어할 수 없는 여러 시나리오가 있습니다. 여기에는 다음이 포함됩니다.

  • 사용자가 브라우저를 종료했다가 다시 시작할 때
  • 사용자가 탭을 복사할 때
  • 사용자가 탭을 닫았다가 다시 열 때

이러한 경우 일부 브라우저에서 원래 탐색 유형이 유지될 수 있으므로 뒤로/앞으로 탐색이 아니더라도 back_forward 유형이 표시될 수 있습니다.

이러한 제외가 없어도 메모리 보존을 위해 일정 기간이 지나면 bfcache가 삭제됩니다.

따라서 웹사이트 소유자는 모든 back_forward 탐색에 대해 100% bfcache 적중률을 기대해서는 안 됩니다. 하지만 이 비율을 측정하면 페이지 자체가 뒤로 탐색 및 앞으로 탐색의 상당 부분에서 bfcache 사용을 방해하는 페이지를 식별하는 데 유용할 수 있습니다.

Chrome팀은 개발자가 bfcache 적중률을 개선할 수 있도록 페이지에서 bfcache를 사용하지 않는 이유를 노출하는 데 도움이 되는 NotRestoredReasons API를 추가했습니다. 또한 Chrome팀은 CrUX에 탐색 유형을 추가하여 직접 측정하지 않고도 bfcache 탐색 수를 확인할 수 있습니다.

실적 측정

bfcache는 현장에서 수집된 성능 측정항목, 특히 페이지 로드 시간을 측정하는 측정항목에 부정적인 영향을 미칠 수도 있습니다.

bfcache 탐색은 새 페이지 로드를 시작하는 대신 기존 페이지를 복원하므로 bfcache가 사용 설정되면 수집된 총 페이지 로드 수가 감소합니다. 하지만 중요한 것은 bfcache 복원으로 대체되는 페이지 로드가 데이터 세트에서 가장 빠른 페이지 로드였을 가능성이 높다는 것입니다. 이는 뒤로 및 앞으로 탐색이 정의상 재방문이며, 재방문 페이지 로드가 일반적으로 신규 방문자의 페이지 로드보다 빠르기 때문입니다(앞서 언급한 HTTP 캐싱으로 인해).

그 결과 데이터 세트에서 빠른 페이지 로드가 줄어들고, 사용자가 경험한 실적이 개선되었음에도 불구하고 분포가 더 느리게 왜곡될 수 있습니다.

이 문제를 해결하는 방법에는 몇 가지가 있습니다. 하나는 모든 페이지 로드 측정항목에 각각의 탐색 유형(navigate, reload, back_forward, prerender)을 주석으로 추가하는 것입니다. 이렇게 하면 전체 분포가 부정적으로 왜곡되더라도 이러한 탐색 유형 내에서 성능을 계속 모니터링할 수 있습니다. 첫 바이트까지의 시간(TTFB)과 같이 사용자 중심이 아닌 페이지 로드 측정항목에는 이 접근 방식을 사용하는 것이 좋습니다.

코어 웹 바이탈과 같은 사용자 중심 측정항목의 경우 사용자 환경을 더 정확하게 나타내는 값을 보고하는 것이 더 좋습니다.

Core Web Vitals에 미치는 영향

Core Web Vitals는 다양한 측정기준(로드 속도, 상호작용, 시각적 안정성)에서 웹페이지의 사용자 환경을 측정합니다. 사용자가 전체 페이지 로드보다 bfcache 복원을 더 빠른 탐색으로 경험하므로 Core Web Vitals 측정항목에서 이를 반영하는 것이 중요합니다. 결국 사용자는 bfcache가 사용 설정되었는지 여부는 중요하지 않으며 탐색이 빠른지만 중요하게 생각합니다.

Chrome 사용자 환경 보고서와 같이 Core Web Vitals 측정항목을 수집하고 보고하는 도구는 bfcache 복원을 데이터 세트에서 별도의 페이지 방문으로 간주합니다. bfcache 복원 후 이러한 측정항목을 측정하기 위한 전용 웹 성능 API는 없지만 기존 웹 API를 사용하여 값을 대략적으로 추정할 수 있습니다.

  • 최대 콘텐츠 페인트(LCP)의 경우 프레임의 모든 요소가 동시에 페인트되므로 pageshow 이벤트의 타임스탬프와 다음 페인트된 프레임의 타임스탬프 간의 차이를 사용합니다. bfcache 복원의 경우 LCP와 FCP는 동일합니다.
  • 다음 페인트에 대한 상호작용(INP)의 경우 기존 Performance Observer를 계속 사용하지만 현재 INP 값을 0으로 재설정합니다.
  • 누적 레이아웃 변경(CLS)의 경우 기존 Performance Observer를 계속 사용하지만 현재 CLS 값을 0으로 재설정합니다.

bfcache가 각 측정항목에 미치는 영향에 관한 자세한 내용은 개별 Core Web Vitals 측정항목 가이드 페이지를 참고하세요. 이러한 측정항목의 bfcache 버전을 구현하는 방법에 관한 구체적인 예는 web-vitals JS 라이브러리에 이를 추가하는 PR을 참고하세요.

web-vitals JavaScript 라이브러리는 보고하는 측정항목에서 bfcache 복원을 지원합니다.

추가 리소스