들어가며
오랜만에 글을 쓰게 되었다. 최근 여러 이슈들이 겹치고 바쁜 일상 속에서 자연스럽게 게을러졌고, 공부에도 소홀해지면서 결국 몇 달간 글을 쓰지 못했다. 하지만 이제 회사 프로젝트가 마무리되었고, 그 과정에서 여러 문제들과 마주치게 되었는데, 오늘은 그중 하나인 BFcache 이슈에 대해 포스팅 하려고 하는데 들어가기전 짧게 설명해 보자면..
간단하게 배경을 설명하자면, 우리 회사는 AOS와 iOS를 지원하며, Mobile Web과 App 플랫폼을 함께 제공하고 있다.
이번 이슈는 ios safari 에서 일어난 이슈인데 (QA에서 ios 붙으면 숨이 턱턱막히는..)
간단하게 플로우를 설명하자면 페이지A > 페이지 B로 이동> 페이지B submit confirm 노출 후 취소한뒤 뒤로가기 > 페이지A > 다시 페이지B로 이동 후 confirm을 노출시키려고하면 응답이 없는상태인데.. 처음엔 읭? 뭐지대체 무슨일이 일어난건지 했다..
처음에는 이게 무슨이슈인지 몰라서 무작정 console.log를 찍고 배포하고(우리 회사는 local에서 safari는 접근 허용을 안해둠) 작업을 하였고, console.log는 정상적으로 노출이 되는걸 확인하고 몇번의 테스트 결과 alert과 confirm창이 안뜬다는걸 확인한 후 바로 구글링을 돌렸는데 그 때 바로 BFcache에 관하여 알게 되었다.
짧게 설명하려고 했지만 조금 길어졌는데, 이제 본격적으로 이 이슈에 대해 자세히 설명해보려 한다.
BFCache란?
BFCache(Back/Forward Cache)는 사용자가 페이지를 뒤로가거나 앞으로갈 때, 페이지를 더 빠르게 로드할 수 있도록 하는 브라우저의 성능 최적화 기능인데. 이 기술은 기존 페이지의 전체 상태를 스냅샷으로 저장한 후, 사용자가 다시 그 페이지로 돌아올 때 해당 스냅샷을 불러와 페이지를 즉시 복원한다고 한다. 이 과정에서 서버에 새로운 요청을 보내지 않기 때문에, 페이지 로딩 시간이 크게 줄어들고 서버 부하도 감소한다.
이러한 캐싱 방식은 특히 Safari와 Firefox에서 기본으로 활성화되어 있다. Chrome은 BFCache를 기본적으로 사용하지 않지만, 수동으로 활성화할 수 있다.
스냅샷이란?
여기서 말하는 스냅샷은 단순히 HTML과 CSS만을 저장하는 것이 아니고, 페이지의 전체 메모리 상태를 그대로 보존하는 것이라고 한다. 자바스크립트 실행 상태, DOM 요소들, 그리고 각종 인터랙션까지 그대로 담기며, 뒤로가거나 앞으로갈 때 그 상태를 그대로 복원한다. 덕분에 페이지 전환이 매우 빠르지만, 동적 데이터를 최신 상태로 유지해야 하는 페이지에서는 문제가 발생할 수 있다.
BFCache와 HTTP 캐시의 차이점
BFCache는 위의 설명대로 전체 상태를 저장하는 반면, HTTP 캐시는 특정 HTTP 요청에 대한 응답 데이터만 캐싱한다는 점에서 차이가 있다. 다시 말해, BFCache는 자바스크립트의 실행 상태, DOM 상태, 그리고 페이지 내에서 발생한 인터랙션(상호작용)까지 모두 포함한 메모리 스냅샷을 저장한다는 것이다.
BFCache의 메모리 사용과 성능 이슈
메모리에 저장한다면 아무래도 성능이슈와 관련이 있을 것 같아서 찾아 보았고, 역시나 여러 이슈들이 있었다.
(왜 Chrom에서 사용하지 않고 safari가 성능 이슈가 있는지 알게 되었다.)
- 메모리 사용 증가
BFCache는 페이지의 전체 상태를 메모리에 저장한다. 여기에는 DOM 구조, 자바스크립트 힙 메모리, 스타일, 그리고 사용자의 인터랙션 상태가 포함된다. 따라서, 많은 페이지를 BFCache에 저장할수록 메모리 사용량이 급격하게 늘어난다. 이로 인해 메모리 부족 문제가 발생할 수 있으며, 특히 메모리가 제한된 모바일 기기나 저사양 디바이스에서는 성능이 저하될 가능성이 높다. - 스냅샷 복원의 비용
페이지가 다시 로드되지 않고 스냅샷을 복원하기 때문에 일반적인 페이지 로딩보다 빠르게 작동하지만, 복잡한 페이지일수록 이 복원 과정 자체도 어느 정도의 자원을 소모한다. 특히 페이지가 복잡한 UI 상태나 대량의 데이터 처리를 포함하고 있을 경우, BFCache에서 복원하는 데 시간이 걸릴 수 있으며, 이 과정이 느려지면 성능 이슈로 이어질 수 있다. - 장기적 메모리 누수 가능성
브라우저가 메모리를 효율적으로 관리하지 못할 경우, 오랜 기간 동안 많은 페이지가 BFCache에 저장되면서 메모리 누수로 이어질 수 있다. 일반적으로 브라우저는 메모리가 부족해지면 오래된 페이지의 BFCache를 삭제하지만, 특정 상황에서는 브라우저의 메모리 관리가 완벽하지 않을 수 있다. - 모바일 환경에서의 성능 이슈
모바일 기기는 데스크탑에 비해 메모리와 성능 자원이 제한적이므로, BFCache로 인해 더 쉽게 성능 문제가 발생할 수 있다. 특히, iOS Safari는 BFCache가 기본적으로 활성화되어 있기 때문에, 페이지 간 전환이 빈번한 모바일 웹 앱에서 성능 저하를 체감할 수 있다.
그렇다면 이번 이슈는?
자바스크립트 힙(메모리)과 페이지의 모든 상태를 그대로 저장한 후, 뒤로가기를 할 때 이 상태를 그대로 불러와 페이지를 재구성 하기 때문에, 페이지가 다시 새로 로드되거나 자바스크립트의 이벤트가 재실행되는 것이 아니라 이전 상태로 복원되기 때문에 alert이나 confirm이 다시 호출되지 않는 거로 생각할 수 있다. 즉, alert이나 confirm을 띄우는 코드가 새로 실행되지 않으면서 창이 뜨지 않는 것이다. (쓰면서도 솔직히 정확한 내부 동작원리를 파악하고 싶은데 거기까진 못찾았다.)
해결방법
해결방법은 여러 구글링을 통해 확인해 보았고, 굳이 정리해서 적을 필요는 없다고 생각한다.
본인이 회사에서 해결한 방법만 간단하게 적으려고한다. (기본적인 해결방법은 gpt나 구글링에서도 많이나옴. 본인 프로젝트 컨벤션이나 구조에 맞게 해결하면 될 것 같다.)
우리 회사 프로젝트는 Next.js 12를 기반으로 하고 있고, Next.js 12 에서는 Page Router를 활용하고 있다. 이 때, router.push는 클라이언트 사이드 네비게이션을 사용하여 페이지 전환 시 전체 페이지를 새로고침하지 않기 때문에, BFCache 관련 이슈가 발생할 수 있다.(safari 처럼 디폴트가 활성화인 경우) 페이지가 스냅샷으로 저장되어 다시 로드될 때 자바스크립트가 재실행되지 않아서 상태가 갱신되지 않거나 alert/confirm 같은 동작이 트리거되지 않는 것이다.
본인이 해결한 방법으론, react-device-detect 라이브러리를 사용하여 OS 및 브라우저 정보를 감지하고, BFCache가 기본 활성화된 브라우저에서만 window.location.href를 사용해 강제로 페이지를 새로고침하게끔 분기처리를 하였다. 이 방법을 통해 iOS나 Safari 브라우저에서는 BFCache 이슈를 피할 수 있었다.
import { useRouter } from 'next/router';
import { osName, browserName } from 'react-device-detect';
const navigateToPage = (url) => {
const router = useRouter();
// iOS Safari 등 BFCache 이슈가 발생하는 브라우저 확인
const isBFCachedBrowser = osName === 'iOS' && browserName === 'Safari';
if (isBFCachedBrowser) {
// BFCache가 적용되는 브라우저에서는 페이지 전체 새로고침
window.location.href = url;
} else {
// 클라이언트 사이드 내비게이션 (BFCache 이슈 없음)
router.push(url);
}
};
이렇게 ios 이슈를 하나 처리 하였다.. 궁금한건 왜 이런 이슈가 발생했는지, 왜 다른 팀원들은 문제를 경험하지 않았는지에 대한 고민이 있었지만, 이번 프로젝트 프로스세 자체가 워낙 뒤로가고, 다시 컨펌띄우는 상황이 많았어서 기존에 없던 프로세스라 터진게 아닐까..? 라는 생각을 하긴 했었지만.. 좀 신기하긴 했다..
처음에는 어렵게 느껴졌던 문제였지만, 해결하고 나니 BFCache에 대해 깊이 있게 이해할 수 있었고, 비록 몇 줄의 코드로 마무리된 이슈였지만, 이를 통해 프로젝트의 문제 해결 과정과 기술적 이해가 한 단계 발전한 느낌이긴 하다~~~
결론적으로, 이 이슈는 단순한 버그 수정 그 이상으로 프로젝트의 흐름을 분석하고, 특정 환경에서 발생할 수 있는 문제를 이해하고 해결하는 과정이었다고 생각한다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 호이스팅(Hoisting)이란? (0) | 2024.02.12 |
---|---|
[JavaScript] 메모이제이션 (Memoization) (0) | 2024.01.01 |