만족
[Nextjs] bfcache로 페이지가 복원되었을 때 브라우저에 따라 라우터가 업데이트되거나 업데이트되지 않는 현상 본문
[Nextjs] bfcache로 페이지가 복원되었을 때 브라우저에 따라 라우터가 업데이트되거나 업데이트되지 않는 현상
FrontEnd/Nextjs Satisfaction 2023. 1. 1. 21:02//...
const router= useRouter();
useEffect(()=>{
//...
}, [router]);
//...
이런 코드가 있을 때, bfcache로부터 페이지가 복원된다면 어떻게 될 것 같은가?
내 생각에는 당연히 memory snapshot을 복구했으니 router가 업데이트되지 않을 것이라고 생각했다.
실제로 데스크탑 크롬에서는 그렇게 동작한다.
그런데 모바일/데스크탑 사파리와 모바일 크롬, 삼성 브라우저에서는 router가 업데이트된다.
아래 코드를 기준으로 설명할 것이다.
(원본 코드는 https://github.com/NamGungGeon/why-router-is-updated 에서 확인할 수 있다)
// page/index.js
function Home() {
const router = useRouter();
// alert if router is updated or init
useEffect(() => {
alert("router is updated(or init)");
console.log("router is updated(or init)", router);
}, [router]);
// alert if page is restored from bfcache
useEffect(() => {
const checkBfcache = (e) => {
console.log("This page is restored from bfcache?", e.persisted);
if (e.persisted) {
alert("This page is served from bfcache");
}
};
window.addEventListener("pageshow", checkBfcache);
}, []);
return (
<>
<Head>
<title>Create Next App</title>
</Head>
<div>
<h1>Currennt path: {router.asPath}</h1>
<div>
<h3>Inner links</h3>
<ul>
<li>
<button onClick={() => router.replace("/?a=b")}>/?a=b</button>
</li>
<li>
<button onClick={() => router.replace("/?a=b&c=d")}>
/?a=b&c=d
</button>
</li>
<li>
<button onClick={() => router.replace("/")}>/</button>
</li>
</ul>
<h3>Outer links</h3>
<ul>
<li>
<Link as="a" href="https://www.google.com" passHref={true}>
<button>https://www.google.com</button>
</Link>
</li>
</ul>
</div>
</div>
</>
);
}
// prevent '/' be static page (running as dynamic page)
Home.getInitialProps = async ({ req, res }) => {
res?.setHeader("Cache-Control", "private, no-cache, must-revalidate");
return {};
};
export default Home;
router가 업데이트되면 router is updated(or init) 이라는 얼럿이 발생하고,
페이지가 bfcache로부터 복원되면 This page is served from bfcache 라는 얼럿이 발생한다.
또한, https://why-router-is-updated.vercel.app/ 에서 테스트 해볼 수도 있다.
행동 1: router.replace, router.push를 통해 링크 이동 후 외부 링크로 이동
1. https://why-router-is-updated.vercel.app/ 로 접속
2. /?a=b 버튼 클릭
3. /?a=b&c=d 버튼 클릭
4. https://www.google.com 버튼 클릭해 외부 링크로 이동
5. 로딩이 끝나면 뒤로가기 버튼을 눌러 bfcache로 페이지 복원
데스크탑 크롬에서는 bfcache 얼럿만 발생한다. (router 업데이트 안됨)
그러나 데스크탑 사파리(+ 모바일 사파리, 크롬, 삼성 브라우저)에서는 bfcache 얼럿 발생 후
router 얼럿이 발생한다. (router가 업데이트됨)
브라우저에 따라 bfcache로 복원되었을 때 router의 업데이트 여부가 다르다.
그렇다면 데스크탑 크롬에서 bfcache로 복원되면, router는 업데이트되지 않고,
그 외의 브라우저에서는 router가 업데이트된다고 보면 될까?
행동2: 진입 후 바로 외부 링크로 이동 (또는 중간에 새로고침하고 외부 링크로 이동)
router.push, router.replace 를 하지 않고 외부 링크로 진입하거나,
사용했더라도 새로고침을 하고 난 후 외부 링크로 갔다가 돌아오면 어떻게 될까?
마찬가지로 크롬에서는 router가 업데이트되지 않는다.
그런데 이번에는 사파리에서도 router가 업데이트되지 않는다.
조건에 따라, 브라우저에 따라 bfcache로 복원되었을 때 router 업데이트 여부가 다른 것이다.
발생 버전
next 12.x 버전과 13.x(현재 기준 최신 버전 13.1 포함)에서 동일한 이슈가 발생하는 것을 확인했다.
임시 해결책
useEffect의 depth에 router를 걸었다는 것은 path, query가 변경될 때 effect를 발생시키기 위함일 것이다.
따라서 [router.pathname]나 [router.query]를 이용하면 되는데 (전체 url을 감지한다면 router.asPath로 퉁치면 된다),
문제는 router가 업데이트될 때 router.query 오브젝트도 새로 생성한다는 것이다.
알다시피
const query1= {foo: 'bar'};
const query2= {foo: 'bar'};
에서
query1 === query2 는 false이다.
따라서 [router.query]를 설정하게 되면,
쿼리가 동일함에도 bfcache로 페이지를 복원했을 때 effect가 발생하게 된다.
이 경우 https://www.npmjs.com/package/use-deep-compare 와 같이 deep compare를 하는 방법을 사용해서,
정말로 query가 변경된 것인지를 판단하여 effect를 발생시켜야 한다.
Nextjs 이슈 등록
의도된 것인지, 버그인 것인지는 아직 모른다.
하지만 내 생각에는 브라우저마다 다르게 동작할 이유가 없는 동작인 것 같아 제보했다.
https://github.com/vercel/next.js/issues/44477
이 오류 때문에 회사일에서 문제가 생겼었는데, 일주일넘게 원인조차 파악하지 못해서 너무 스트레스 받았었다 ㅠㅠ
'FrontEnd > Nextjs' 카테고리의 다른 글
[Nextjs] 로컬 개발 환경(localhost)에 https 적용하기 (1) | 2022.07.24 |
---|---|
[Nextjs] express 프로젝트에 Nextjs 추가하기 (0) | 2021.08.21 |