BFCache
- 사용자가 브라우저 내에서 뒤로가기/앞으로가기를 할 때 이전 페이지를 자바스크립트 heap 영역까지 전체 캐싱하여 메모리에 저장하는 것
- HTTP 캐시는 리소스만을 캐시하고 JS 작업까지 캐시하지 않기에 BFCache보다 빠르지 않다.
BFCache 작동을 판단할 수 있는 API
pageshow, pagehide
pageshow
- 페이지가 처음 로드 될 때, bfcache에서 페이지가 복원될 때마다 load 이벤트 직후 트리거 된다.
- event.persisted === true이면 BFCache에서 복구된 것이다.
pagehide
- 페이지에서 나가거나, 브라우저가 페이지를 bfcache에 저장하려고 할 때 트리거 된다.
- event.persisted === false이면 bfcache에 저장되지 않았다는 뜻이다.
- true라고 항상 bfcache에 저장되지 않는다. (브라우저가 저장하려고 했다는 알수 있음.)
freeze / resume
크롬에서 지원되는 이벤트
freeze
- pagehide 이벤트의 persisted 가 true일 경우, pagehide 이벤트 다음에 호출됨.
- bfcache에 저장하려고 한 의도를 알 수 있음.
resume
- BFCache로 들어가고 나올 때 호출되는 이벤트 (이외 상황에서도 호출됨 ex : CPU 사용량 최소화를 위해 백그라운드 탭이 동결된 경우)
- bfcache에서 페이지가 복원 될 때, 정지된 배경 탭을 다시 방문할 때도 실행
BFCache를 위한 페이지 최적화 방법
- unload 이벤트 사용하지 않기
- unload 이벤트는 페이지가 더이상 존재하지 않을 거라는 가정하여 운영됨.
- 따라서 unload 이벤트 사용시 BFCache는 사용되지 않음. 대신 pagehide 이벤트 사용하기
- window.opener 참조를 피하라
rel="noopener"
를 사용하지 않고 열면 새로운 페이지는 window.opener를 가지고 있고, 이 opener를 가지고 있는 페이지는 bfcache에 넣지 않는다.Tabnabbing
보안 취약점 공격과도 연관된 부분이기 때문이다. (Tabnabbing 공격)
- navigate 전에 항상 open된 connection 닫기.
- setTimeout, promise 함수가 진행중일때 bfcache에 삽입하면, 작업을 일시정지하고 다시 페이지에 돌아왔을 때 진행중인 작업을 재개한다.
- 스케쥴된 작업이 단지 DOM APIdㅔ 접근하는 등 해당 페이지에만 영향을 주는 API라면 일시 정지 후 재개가 문제되지 않는다.
- 하지만 same-origin의 다른 페이지에서도 액세스할 수 있는 API의 경우 작업이 일시 중지되었을 때, 다른 탭의 코드가 실행되지 않을 수도 있기 때문에 문제가 발생한다.
- 따라서 많은 브라우저에서는 connection이 open되어있을 경우 bfcache에 해당 페이지를 넣지 않는다.
- IndexedDB 트랜잭션 중일때
- fetch(), XMLHttpRequest 중일때
- WebSocket, WebRTC 연결이 열려있을 때.
- 최적화를 위해선, pagehide나 freeze 이벤트에서 항상 connection을 닫고, observer를 제거하거나 연결을 끊는게 좋다. 물론 다시 돌아왔을 때 연결을 맺어줘야한다.
bfcache 비활성화하기
아래와 같이 Cache-Control 속성을 설정하면 된다.
1
Cache-Control: no-store
물론 다른 캐시들도 동작하지 않는다.
BFCache 문제점
- 성능측정을 불분명하게 함 : bfcache에서 페이지가 로드되면 매우 빠르게 로드되기에 성능 측정 시 잘못된 결과값을 내게할 수 있다.
- 집계를 내는 것이 힘듬 : bfcache로 다시 방문했을때 페이지 접속 집계가 안될 수도 있음.
두 경우 모두 pageshow
이벤트를 사용하여 정상적으로 수행하도록 해야한다.
출처
https://blog.naver.com/PostView.nhn?blogId=qls0147&logNo=222157982248&categoryNo=0&parentCategoryNo=0&viewDate=¤tPage=1&postListTopCurrentPage=1&from=postView