웹 페이지에서 뒤로가기 캐시 때문에 이슈가 있었고 해결했던 과정을 담았습니다. (BFCache, BFCache, Back-Forward Cache)
제가 겪었던 이슈를 공유하기 전 BFCache란 무엇인지 알아보겠습니다.
뒤로가기 캐시(BFCache, Back-Forward Cache) 란?
웹 브라우저의 뒤로가기 버튼을 사용할 때 이전에 방문한 페이지의 상태를 캐시해두는 기능입니다.
브라우저는 해당 페이지의 상태와 DOM 트리를 저장하여 BFCache에 보관합니다. 그런 다음 사용자가 뒤로가기 버튼을 클릭하면, 이전 페이지의 캐시된 상태를 가져와서 페이지를 빠르게 복원합니다.
이 과정에서 다시 네트워크 요청을 보내지 않고, 캐시된 리소스를 사용하므로 페이지 로딩 시간이 단축되어 사용자 경험을 향상시킵니다.
아래 내용을 참고하면 좋을 것 같습니다.
- BFCache는 브라우저마다 다를 수 있는 기능이므로, 모든 브라우저에서 동일하게 작동하는 것은 아닙니다. 일부 브라우저는 BFCache를 지원하지 않거나, 사용자 설정에 따라 비활성화할 수도 있습니다.
- BFCache는 주로 단일 페이지 애플리케이션(SPA, Single-Page Application)과 같은 동적인 웹 애플리케이션에 적합합니다. 정적인 페이지나 서버에서 동적으로 생성되는 페이지의 경우에는 BFCache의 이점을 누리기 어려울 수 있습니다.
- 브라우저는 BFCache를 사용하기 위해 페이지의 상태와 DOM 트리를 캐시해야 하므로, 메모리 사용량이 증가할 수 있습니다. 따라서 메모리 제한이 있는 환경에서는 BFCache를 비활성화하는 것이 좋을 수도 있습니다.
0. 이슈 상황
회사 홈페이지에서 문의하기 기능을 구현을 맡았습니다.
이때, 문의는 한번에 완료가 아니라 단계별로 입력 후 마지막 페이지에서 완료 되는 기능입니다.
ex.) type을 A로 선택 후 [다음]을 누르면 가격 선택하는 페이지 [다음]을 누르면 위치 선택하는 페이지
1. 문제 발생
다른 환경은 괜찮았지만 아이폰 사파리에서 단계별로 입력중에 브라우저 뒤로가기를 통해 “뒤로가기, 값 선택, 뒤로가기, 값 선택” 반복할 경우 alert이 뜨지 않았습니다.
2. 원인파악 및 개선시도
- 다음페이지로 이동하는 버튼을 인식 못하는건가 의심
=> console 찍은 결과 버튼 클릭은 인식 - window.alert이 안되는것인가 의심
=> typeof window.alert을 console로 찍어봄 결과는 function으로 잘 나옴 - 구글에 검색 ‘사파리 alert’ 여러 글을 보다가 BFCache라는 것이 있다는 것을 알게 됨.
2-1. 해결중
useEffect(()=> {
window.addEventListener('onpageshow', (event) => {
if ( event.persisted || (window.performance && window.performance.navigation.type == 2)) {
window.location.reload()
}
})
},[])
1) 위 코드에서 이벤트의 속성중 persisted가 true일 경우 bfcache가 있는 상태. 이럴경우 페이지 리로드를 해준다.
⇒ 결과: React + Next.js 환경이 프로젝트에서는 onpageshow의 이벤트를 인식하지 못한다. 페이지가 새로 로드되는것이 아니기 때문에..
useEffect(()=> {
window.addEventListener('popstate', (event) => {
if ( event.persisted || (window.performance && window.performance.navigation.type == 2)) {
window.location.reload()
}
})
},[])
이벤트의 속성중 bfcache를 사용하고 있는지 아닌지에 대한 여부를 알 수 없다…
2-2. 해결
useEffect(() => {
const contact = getLocalStorage()
if (contact) {
if (contact.loaded) {
console.log('reload')
window.location.reload()
}
window.localStorage.removeItem('contact')
}
}, [])
올바른 방법인지 모르겠지만 type선택하는 화면에서 localStorage에 contact안에 loaded속성이 true이면 reload를 해주는 방향진행했다.
useEffect(() => {
window.localStorage.setItem('contact', JSON.stringify({ loaded: true }))
}, [])
그리고 type 선택 후 step1로 넘어갈 경우 contact.loaded에 true를 넣어주었다.
**이렇게 한 이유:
type선택하는 화면을 열때만다 reload 할 필요는 없고 step1이상을 진행했다가 다시 뒤로가기 했을 경우에만 reload를 해주어야 하기때문에
첫 화면에서 contact.loaded의 여부를 조건으로 넣었고 그 다음 step1화면에 넘어가면 localStorage에 contact.loaded = true를 해주었습니다.
그렇게되면 다른페이지에서 type선택 화면을 접근시 reload를 안해주고 step1진입 후 뒤로가기를 눌렀을 때 reload를 해주게 됩니다.**
뭔가 정석은 아닌 것 같지만..alert이 뜨지 않는 현상은 일어나지 않았습니다!! 🙌
2-3. 다른 문제 발생
페이지를 reload하는 화면에 접근하지 않으면 동일 현상이 일어난다. ex.) step2 → step3 → step2
찾아보니 BFCache는 브라우저의 내부 기능으로 동작하기 때문에 JavaScript나 HTML에서 직접적으로 제어할 수는 없다고 합니다..🥲