내가 이 회사 와서 제일 처음 구현한게 바로 scroll restore 기능이다. custom hook으로 제작했으며..개발 과정에 있던 내 삽질 과정을 적어보려고 한다.
분명 공식문서에는 Nextjs가 scroll restoration을 지원한다고 적혀있다. 근데 공식문서에서 하라는 그대로~ 해도 안됐다. 왜인지는 아직도 모르겠다. 구글링해서 나오는 각종 방법도 써봤는데 되는 페이지가 있고 안되는 페이지가 있고 아주 각양각색이었다. 아시는 분 있으시면 제발 누가 알려주면 좋겠다.
저번 회사에서도 같은 기능을 만들었던 적이 있다. 그 당시엔
{ url: { ..., scrollY: 689 } }
구현도 간단했고 스크롤 복원 이라는 표면적인 것에만 초점을 맞춘다면 상당히 잘 동작했다. 하지만 문제가 있었다. 뒤로가기 시에만 스크롤이 복원되어야하는데 아니여도 이전 스크롤이 복원된다는 것이었다.
이번엔 만들면서 저번보다 두가지를 개선하고싶었다.
local storage는 동일하게 사용해도 됐을 것 같지만 이번 회사에선 redux
를 사용하고 있어서 store에 저장해서 쓰기로 했다.
1단계
처음에는 직전 뒤로 가기만 생각해서 만들었다. 그래서 redux store에도 scroll을 그냥 한번만 저장하고 날렸다.
routeChangeStart
이벤트가 발생했을 때 스크롤 정보를 저장하고
routeChangeComplete
이벤트가 발생하면 스크롤을 복원했다.
2단계
하지만 홈 => 제품 목록 => 제품 => 제품 브랜드 이런식으로 스크롤 히스토리가 쌓이는 경우 뒤로 갈 때마다 스크롤 복원이 필요해서 store에서 스택처럼 pop, push를 진행했다.
3단계
라우터 이동 시마다 스크롤을 위치를 저장했더니 뒤로가기를 눌렀을 때도 스크롤이 불필요하게 저장되었다. 그래서 shouldSaveScroll
이라는 변수를 하나 추가해서 popState
가 아닐 때만 스크롤 상태를 저장했다.
완성된 코드 일부를 보면 아래와 같다!
...
useEffect(() => {
if (!"scrollRestoration" in window.history) return;
let shouldSaveScroll = true;
window.history.scrollRestoration = "manual";
const onRouteChangeStart = () => {
if (!shouldSaveScroll) return;
saveScrollPos(); //
};
const onRouteChangeComplete = () => {
restoreScrollPos();
};
Router.beforePopState(() => {
shouldSaveScroll = false;
return true;
});
Router.events.on("routeChangeStart", onRouteChangeStart);
Router.events.on("routeChangeComplete", onRouteChangeComplete);
return () => {
Router.beforePopState(() => true);
Router.events.off("routeChangeStart", onRouteChangeStart);
Router.events.off("routeChangeComplete", onRouteChangeComplete);
};
}, [router]);
...
확실히 이전 회사에서 구현했던 방식보다 더 나은 방법인 것 같다. 그땐 라우터 이벤트도 활용하지 못 했는데 이번에는 확실하게 활용할 수 있었다. 아직도 쪼랩이지만 조금은 덜 쪼랩이된것 같아 기뿌다.
https://nextjs.org/docs/api-reference/next/router#routerevents