[Next.js] 뒤로가기 시, 스크롤 위치 복원하기

IN DUCK KANG·2023년 3월 24일
4

Next.js 기록

목록 보기
1/3

경험적으로 그리고 상식적으로 웹 사이트 혹은 앱을 이용할 때, 다음페이지로 이동 시 해당 페이지의 스크롤은 맨 위이여야 하고, 뒤로가기 시 그 이전 페이지의 이동 전 스크롤 위치로 돌아가야 한다.

하지만 Next.js 프로젝트를 생성하고 페이지 이동을 해보면, 뒤로가기 시 이전 페이지의 스크롤 위치가 복원되지 않는다는 것을 알 수 있다. Next.js에서 화면 이동시 사용하는 router.push는 단순히 path만 이동시켜주는 기능이기 때문이다.

추천하지 않는 방식

굉장히 필수적인 기능인데 Next.js에서 해당 기능을 설마 구현 안했을까 하는 마음으로 서칭을 해보니 다음과 같은 실험적인 기능이 있는것을 찾았다.

const nextConfig = {
  experimental: {
    scrollRestoration: true,
  },
};
module.exports = nextConfig;

그러나 이 설정만으로 잘 동작할 것이라는 기대와는 달리, 모든 상황에서 잘 동작하지 않았고, 이 방법은 현재도 불안정 하기 때문에 추천하지 않는다.
다른 방법을 찾아봐도 마땅히 좋은 해결 방안이 없어서, 직접 스크롤 복원 로직을 구현하기로 했다.

추천하는 방식

구현하고자 하는 로직은 다음과 같다.

1. 다음 페이지로 이동 직전, 현재 스크롤 위치를 저장하고 이동한다.
2. 뒤로가기 시, 페이지 이동 직후 저장된 이전 스크롤 위치로 스크롤을 이동시킨다.

스크롤 위치 저장소

이를 구현하기에 앞서 해야 할 고민은 어느 저장소에 저장을 해야 할지이다. 데이터를 저장할 수 있는 곳은 브라우저 저장소(LocalStorage, SessionStorage, Cookie)와 프론트엔드 레벨의 상태관리 스토리지가 있다.

저장된 스크롤의 위치 데이터는 새로고침을 해도 내역이 유지 되어야 하며, 창을 닫았을때 초기화 되어야 하고, 브라우저간 공유가 되면 안되기 때문에 SessionStorage 가 적합하다고 판단이 되었다.

페이지 route 전, 현재 스크롤 위치 저장

react-router에 의해 window.history.state.idx에 숫자 데이터가 기록되어 있는데, 현재 페이지가 몇번 째로 이동한 페이지 인지를 알 수 있다. 따라서 이 페이지 숫자를 sessionStorage의 키로 활용하기로 하고 페이지를 이동하기 직전에 다음과 같은 코드를 이용해서 스크롤 위치를 저장 후 이동하도록 했다.

 sessionStorage.setItem(
    `__next_scroll_${window.history.state.idx}`,
    JSON.stringify({
      x: window.pageXOffset,
      y: window.pageYOffset,
    })
  );

스크롤 위치 복원

이제 뒤로가기 시, 저장되어 있는 스크롤의 위치로 스크롤을 복원시켜야 한다.
scrollRestoration을 manual로 설정하여, 스크를 위치 복원을 수동으로 관리 하겠다고 선언을 하고, Next.js의 routeChangeComplete event를 이용하여 페이지 이동 직후 스크롤 복원 로직을 적용하였다.
이때 routeChangeComplete는 페이지를 앞으로 이동한 경우데도 함수가 실행이 되지만 해당 페이지의 번호(window.history.state.idx)로 저장되어 있는 정보가 없기 때문에 pass 된다.
즉, 뒤로가기 시에만 스크롤 위치가 복원이 된다.

useEffect(() => {
  window.history.scrollRestoration = 'manual';
  
  // 페이지 이동 후 저장되어 있던 위치로 스크롤 복원
  const _scroll = sessionStorage.getItem(`__next_scroll_${window.history.state.idx}`);
  if (_scroll) {
    // 스크롤 복원 후 저장된 위치 제거
    const { x, y } = JSON.parse(_scroll);
    window.scrollTo(x, y);
    sessionStorage.removeItem(`__next_scroll_${window.history.state.idx}`);
  }
  
  router.events.on('routeChangeComplete', routeChangeCompleteHandler);
  return () => {
    router.events.off('routeChangeComplete', routeChangeCompleteHandler);
  };
}, []);

현재까지는 이게 가장 좋은 방법이라는 생각이 드는데, 더 좋은 방안이 있을지는 계속 고민중이다..

profile
Web FE Developer

0개의 댓글