뒤로가기 시 페이지의 데이터가 최신화되지 않는다던가, 뒤로가기 시 문제가 있다는 이슈가 생기고 또 그 이슈가 사파리 브라우저나 IOS 웹뷰에서 발생한다는 말을 들으면 딱 생각나는 단어가 있다. (BFCache!!)
"Back/Forward Cache(BFCache)"는 페이지 전체를 스냅샷 떠놨다가 뒤로가기 시 해당 스냅샷을 그대로 사용하는 캐싱 기술이며, 사파리와 파이어폭스에서 기본적으로 Enable 되어있다. 크롬에서는 현재 기준으론 기본적으로 사용되지 않고, 수동으로 켤 수는 있다.
이 글에서는 프론트엔드 개발자를 괴롭히는 BFCache가 무엇인지, 그리고 해결 방법이 무엇인지 정리하고자 한다.
BFCache 란?
BFCache 가 사용되는 브라우저의 경우, 다른 페이지로 이동하는 순간 기존 페이지 전체 스냅샷(자바스크립트 힙 영역을 포함해서)_을 메모리에 저장하고, 뒤로가기나 앞으로가기를 이용해 돌아갈 때 BFCache에 저장되있는 스냅샷으로 페이지를 빠르게 복원해준다.(아래부터는 뒤로가기만 얘기하겠다. 앞으로가기보단 뒤로가기를 훨씬 많이 사용하기 때문에..)
따라서 BFCache 자체는 뒤로가기 시 불필요하게 페이지를 다시 서버로 요청하지 않아도 되기 때문에 페이지가 뜨는 속도도 훨씬 빠르고 서버 부하도 덜어주는 좋은 기술이다.
HTTP 캐시와는 다른 개념이니 혼동하면 안된다. HTTP 캐시는 HTTP 요청에대한 응답을 캐시한다.
문제는..
위와 같이 좋은 기술이지만 자바스크립트 힙 영역을 포함한 페이지 전체 스냅샷을 떠버리기 때문에, 개발자를 괴롭히는 문제가 되기도 한다. 해당 페이지가 백퍼센트 서버사이드에서 렌더링되어 응답되는 페이지가 아닌 이상, API 를 요청해서 응답된 데이터로 페이지가 구성되는 경우가 많은데 BFCache 때문에 페이지가 로딩됐을 때 자바스크립트에서 호출되는 API 요청이 요청되지 않을 수 있다. 리액트를 사용한다면 useEffect 훅도 실행되지 않는다..
또 페이지에서 모달이나 툴팁같은 게 떠있는 상태에서 페이지 이동을 했다가 뒤로가기로 돌아오면 모달이나 툴팁이 떠있는 상태일 수 있다. 즉, BFCache로 인해서 원하지 않는 동작이 일어날 수 있고, 데이터가 최신화되지 않을 수 있다. 개발할 때 페이지가 정상적으로 로드되는 상황만 고려해서 개발을 하게 되면 BFCache로 인해서 이런 이슈를 만나게 될 수 있다.
어떻게 해결해야 할까?
다행히 BFCache에서 복원되는 경우를 확인 할 수 있는 API가 있다. pageshow
, pagehide
이벤트를 이용하여 bfcache 여부를 확인 할 수 있다.
pageshow 이벤트
pageshow 이벤트는 페이지가 로드되거나, bfcache에서 페이지가 복원될 때 호출된다. 그리고 Callback에 전달되는 event 객체는 persisted 속성을 가지고 있는데 이 속성은 bfcache에서 페이지가 복원되었을 때 true이다.
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
// bfcache로 페이지가 복원되었을 때 실행해야하는 로직을 넣어준다.
} else {
// persisted가 true가 아닌 경우는 정상적으로 페이지가 로드된 경우다.
}
});
pagehide 이벤트
pagehide 이벤트는 pageshow 이벤트와 반대이다. pagehide 이벤트는 페이지가 unload 되거나, bfcache 로 페이지가 캐시되기 직전 호출된다. 이 때 주의해야 할 점은 event.persisted 가 true라고 해도 bfcache가 되지 않을 수도 있다는 점이다. 다른 외부요인으로 인해 bfcache가 되지 않을 수도 있다.
window.addEventListener('pagehide', (event) => {
if (event.persisted === true) {
// bfcache로 이 페이지가 *아마도* 캐시 될 것이다.
} else {
// 이 페이지는 정상적으로 unload 된다.
}
});
주로 pageshow 이벤트를 이용하여 bfcache 시에 꼭 필요한 API 요청을 해준다거나, 떠 있는 모달 혹은 툴팁을 숨긴다거나 하는 로직을 넣어주면 된다.
하지만 이런 로직이 굉장히 복잡한 경우에는 하나 하나 넣어주는 게 불가능 할 수도 있다. 이런 경우에는 슬프지만 그냥 아래처럼 페이지를 새로고침 해주는 경우도 있는 것 같다...(속도와 성능은 포기한다)
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
location.reload();
}
});
마치며..
bfcache는 분명 사용자 경험을 향상시켜주고, 성능도 세이브할 수 있는 좋은 기술임에는 틀림없지만, 동시에 전체 스냅샷이라는 특성 때문에 프론트엔드 개발자를 골머리 아프게 하는 존재이기도 하다. 개발을 하는 입장에서는 스트레스 유발자 이지만, 모바일 시대에 사용자 기기의 데이터 사용량에 대한 고민도 필요하고 네트워크 환경이 느린 환경에서는 굉장히 큰 차이가 날 수 있기 때문에 이런 부분도 고민하고 안고 가야할 것 같다.