개발자

nextjs scroll restoration (스크롤 유지)

2022년 09월 15일조회 3,372

혹시 next.js에서 스크롤 유지 (뒤로가기 시, 전 페이지의 스크롤 유지) 기능을 구현해보신 분 계신가요? next.js에서 실험적으로 설정을 켜면 되는 것 같긴한데 (https://mmazzarolo.com/blog/2021-04-10-nextjs-scroll-restoration/), 뭔가 실험적인 기능인 것 같아서 써도 되는지 잘 모르겠어서요. 혹시 깡(?) js로 구현해보신 능력자분 있으시면 지나가면서 아이디어 한번씩 부탁드립니다. (보통 이렇게 깡?으로 구현하는게 좋은건가요? 아니면 지양해야할까요?)

이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 3

인기 답변

손정현님의 프로필 사진

비슷한 기능을 구현해본 적이 있긴하지만, 질문자님의 웹 사이트 동작 방식이 어떤지 몰라서 명확한 답을 드릴 순 없을 것 같네요 😟 일단 질문자님이 공유해주신 링크의 원문 중 참조한 링크(https://github.com/vercel/next.js/commit/38bd1a024cb25923d8ea15f269a7294d073684d8)를 보시면 next.js에서 해당 기능을 어떻게 구현했는지 볼 수 있네요. 저라면 요거 좀 더 살펴보고 현재 웹 사이트에 적용했을때 문제 없는지 파악하는데부터 시작할 것 같아요. 일단 막연하게 생각해보면, next.js를 어떻게 사용하는지에 따라서 기능 구현 방법이 달라질 수 있습니다. 예를 들면, 인피니트 스크롤 형식의 페이지에 react-query와 pagination을 적용해서 데이터를 불러오는 페이지의 경우, 뒤로가기를 통해 재방문했을 때 전에 요청했던 pagination page의 데이터까지 보존하고 있어야 DOM의 크기도 그만큼 커질 수 있고 스크롤이 내려갈 수 있겠죠? react-query와 SSR을 사용하신다면 요청한 key의 데이터가 의도치않게 갱신되는 것도 신경 쓰셔야할거구요. 질문자님의 웹 사이트가 어떤 방식으로 돌아가는지 구체적이지 않아서 명확한 답을 드리기는 어렵지만, 시간이 되신다면 공식 문서, 다른 기술 블로그, 스택오버플로우를 찾아보시면서 생각해보시면 좋을 것 같습니다. 개인적으로는 깡으로 구현하는 것보다 라이브러리가 있다면 쓰자 주의긴한데, 이건 상황에 따라 다를 것 같아요 참고로 "커리어리 사이드 프로젝트 페이지" (https://careerly.co.kr/lounge/side-projects)에 스크롤 유지가 적용되어 있는데 CSR과 next.js의 스크롤 유지 기능을 살짝 손봐서 만들었습니다 🙂

profile picture

익명

작성자

2022년 09월 16일

생각보다 고려해야될게 많네요. 역시 쉽지는 않군요. 참고자료 열심히 찾아볼게요. 답변 감사드립니다!

이방원님의 프로필 사진

이방원

아스크스토리디에스 프론트엔드 개발자2023년 04월 24일

작성된지 오래된 글이지만, "커리어리 사이드 프로젝트 페이지"의 스크롤 유지 기능을 살펴보고 저 나름대로 살펴보았는데, 글 리스트에 클릭하고 페이지가 이동하기 전에, session storage 특정 글 리스트의 index값을 set -> 게시글 리스트 페이지로 돌아갔을 때, 글 리스트 데이터를 전부 불러오고, -> session storage에 저장된 index 값을 get해서 scroll 위치를 찾아준 다음 -> session storage에 index 값을 저장했던 key에 null 값을 set한다 라고 생각했습니다. 여기서 궁금한 게 있습니다. 정현 님께서 말씀하신 무한 스크롤 형식의 페이지(reaact query와 페이지네이션을 적용)에서 ssr을 사용하였을 때, 요청한 key의 데이터가 의도치 않게 갱신 된다는 것에 대한, 원인에 대해 궁금합니다!!

손정현님의 프로필 사진

손정현

coya2023년 04월 25일

방원님 안녕하세요! 현재 사이드프로젝트 페이지는 방원님이 파악하신것처럼 무한 스크롤 컴포넌트의 index를 사용하는 방식으로 변경된 상태입니다. 기존에는 scroll height을 저장해놓고 불러오는 로직이었어요 :) (기억을 더듬어서) react-query와의 문제를 설명드리자면 일종의 엣지 케이스였던걸로 기억하구요, 구성을 설명드리자면 사이드 프로젝트 목록 데이터를 getServersideProps에서 queryClient.prefetchQueries로 가져오고 있었고 디폴트 limit이 걸려있었습니다. 마찬가지로 클라이언트 사이드에서 스크롤을 내리면 요청을 날릴 수 있도록 구현되어 있었구요. 서버와 클라이언트 모두 같은 query key를 사용하고 있었어요. 유저가 page 5까지 내렸다가 다른 페이지로 이동/새로고침을 한 뒤 뒤로가기를 하면 getServersideProps가 다시 돌았던걸로 기억합니다. 그래서 디폴트 리밋이 걸려있는 queryClient가 클라이언트로 덮어씌워지면서 기존 page 5까지 받아온 데이터가 날라갔던 현상이었어요. 사용하던 버전은 아마 next 11이랑 react-query v3 였던걸로 기억해요. 적고보니까 이게 정확한 원인이었는지도 살짝 의문이긴하네요. 저때쯤에 제가 react-query 깃허브 질문을 올린적이 있는데 메인테이너 분이 nextjs에서 이런거 하기 좀 힘들수있다라고 했었던 것 같네요. - https://github.com/TanStack/query/discussions/3783#discussioncomment-3392263

이방원님의 프로필 사진

이방원

아스크스토리디에스 프론트엔드 개발자2023년 04월 25일

안녕하세요 정현 님!! 답변 주셔서 감사합니다!! 덕분에 재미있는 에피소드 하나 듣게 되었습니다. 아직 react-query에 대해서 잘 모르고 getServersideProps에 대해서도 잘 몰라, 검색하면서 주신 답변을 읽어보았습니다. getServersideProps가 다시 도는 부분에서 고민을 해야 하는 걸까? queryClient.prefetchQueries를 사용해도 서버에서 새로 데이터를 다 가져오는 경우인가? 그래서 새로운 데이터로 인해 덮어 씌어지는 건가? default limit을 스토로지를 사용해 page 늘어 날 때마다 바꿀 수 있다면 좋을까? 라는 상상을 해보기도 했습니다. 읽으면서 더 배우고 생각하게 되었습니다. 감사합니다!!

쫑님의 프로필 사진

https://www.npmjs.com/package/next-easy-keepalive 제가 만든 keep-alive 입니다 한번 사용해보세요~

커리어리 AI 봇님의 프로필 사진

안녕하세요! Next.js에서 스크롤 유지 기능을 구현하는 방법은 다양하지만, 가장 간단한 방법은 다음과 같습니다. 1. _app.js 파일에 scrollRestoration 설정 추가 2. 아래 코드에서 'scrollRestoration'을 'manua'l로 설정하고, 'cachedScrollPositions' 객체를 통해 각 페이지별 스크롤 위치를 기억합니다. 3. 'beforeunload' 이벤트를 통해 페이지 이동 시점에 스크롤 위치를 저장하고, 'routeChangeComplete' 이벤트를 통해 이전 페이지의 스크롤 위치를 찾아와 복원합니다. 이 방법을 사용하면 실험적인 기능을 켜지 않고도 간단하게 스크롤 유지 기능을 구현할 수 있습니다. 하지만, 이 방법은 페이지 이동 시점마다 스크롤 위치를 저장하므로 메모리 사용량이 증가할 수 있습니다. 이 문제를 해결하기 위해 스크롤 위치 저장을 로컬 스토리지나 쿠키를 사용하여 관리할 수도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { useEffect } from 'react';
import { useRouter } from 'next/router';

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  useEffect(() => {
    // 뒤로가기 시 스크롤 위치를 이전 위치로 복원
    if ('scrollRestoration' in history) {
      history.scrollRestoration = 'manual';
      const cachedScrollPositions = {};

      const onBeforeUnload = () => {
        cachedScrollPositions[window.location.pathname] = [window.scrollX, window.scrollY];
      };
      window.addEventListener('beforeunload', onBeforeUnload);

      const onRouteChangeStart = () => {
        cachedScrollPositions[router.pathname] = [window.scrollX, window.scrollY];
      };
      router.events.on('routeChangeStart', onRouteChangeStart);

      const onRouteChangeComplete = (url) => {
        if (cachedScrollPositions[url]) {
          setTimeout(() => window.scrollTo(...cachedScrollPositions[url]), 0);
        }
      };
      router.events.on('routeChangeComplete', onRouteChangeComplete);

      return () => {
        window.removeEventListener('beforeunload', onBeforeUnload);
        router.events.off('routeChangeStart', onRouteChangeStart);
        router.events.off('routeChangeComplete', onRouteChangeComplete);
      };
    }
  }, [router]);

  return <Component {...pageProps} />;
}

export default MyApp;

커리어리 AI 봇의 답변을 평가해 주세요!

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로
키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!