인프런 커뮤니티 질문&답변

바스니카님의 프로필 이미지
바스니카

작성한 질문수

[리뉴얼] React로 NodeBird SNS 만들기

인피니트 스크롤링 적용하기

infinite scroll에서 scroll 위치 기억하는 법에 대해 질문드립니다.

작성

·

2.5K

1

infinite scroll을 하다가 게시글을 누르면 해당 게시글로 이동하고, 뒤로가기를 누르면 원래 보던 scroll 위치로 이동하도록 만들고 싶습니다.

마치 인스타그램 게시글들을 보다가 프로필을 누르면 프로필 페이지로 이동하고 뒤로가기를 누르면 원래 보던 위치로 돌아가는 것처럼 구현하고 싶습니다.

 

시도한 방법

scroll 위치는 localStorage를 이용해서 저장했습니다. 문제는 게시글들인데 게시물들은 아래와 같이 ssr을 이용해서 미리 불러왔습니다.

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req, query, resolvedUrl, res }) => {
      // login
      const cookie = req.headers?.cookie;
      axios.defaults.headers.Cookie = "";

      if (cookie) {
        axios.defaults.headers.Cookie = cookie;
      }

      store.dispatch({
        type: CHECK_LOGIN_REQUEST,
      });

      // board
      if (
        store.getState().board.previousUrl === null ||
        store.getState().board.previousUrlType !== query.type
      ) {
        store.dispatch({
          type: SAVE_PREVIOUS_URL_TYPE,
          data: query.type,
        });
        store.dispatch({
          type: SAVED_POSTS_INITIALIZE,
        });
        store.dispatch({
          type: LOAD_BOARD_REQUEST,
          data: { type: resolvedUrl },
        });
      }

      store.dispatch(END);
      await store.sagaTask.toPromise();
    }
);

게시글 페이지로 이동하면 지금까지 본 게시글들이 사라지고, 다시 돌아오면 load되면서 scrollHeight가 localStorage에 저장된 scroll 위치보다 작아지면서 이동하지 않는 현상이 발생합니다.

어떻게 하면 되는지 조언 부탁드립니다.

 

답변 4

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

이 부분은 나중에 배우는 swr을 사용하는 게 좋습니다. 브라우저에서 캐시로 갖고 있는지라 빠르게 데이터를 불러올 수 있고, 스크롤 위치를 복원할 수 있습니다. swr에서 infinite scrolling을 제공합니다.

바스니카님의 프로필 이미지
바스니카
질문자

그럼 유저가 다른 페이지에 갔다가 돌아오면 이전까지 본 모든 게시글들을 swr을 통해 가져오는 건가요?

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

swr이 캐싱하고 있다가 불러오는 것입니다.

0

바스니카님의 프로필 이미지
바스니카
질문자

useEffect(() => {
    window.scrollTo(0, 300);
  }, []);

  const scrollPos = () => {
    console.log(window.scrollY);
    localStorage.setItem("y", window.scrollY);
  };

  useEffect(() => {
    window.addEventListener("scroll", scrollPos);

    return () => window.removeEventListener("scroll", scrollPos);
  }, []);

 

image

구글링해도 원하는 대답이 안 나와서 질문드립니다.

scrollTo를 300으로 설정했는데 321은 왜 나오는 건가요?

 useEffect(() => {
    window.scrollTo(0, 300);
  }, []);

  const scrollPos = () => {
    // console.log(window.scrollY);
    localStorage.setItem("y", window.scrollY);
  };

  useEffect(() => {
    console.log(window.scrollY);
    window.addEventListener("scroll", scrollPos);

    return () => window.removeEventListener("scroll", scrollPos);
  }, []);

 

 또 위와 같이 작성하면 window.scrollY가 300으로 출력됩니다. 어디서 제가 놓친걸까요?

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

아래처럼 작성하면 useEffect가 호출될 때 콘솔로그 찍히는 것이고, 위에는 scroll이 발생할 때 찍히는 것이라 당연히 콘솔 기록이 다를 수밖에 없습니다. scroll이 발생할 때 찍히는 게 아무래도 더 정확하겠죠.

바스니카님의 프로필 이미지
바스니카
질문자

제가 스크롤을 하지 않아도 새로고침을 누르면 300으로 찍히다가 바로 321이 찍힙니다. 왜 그런지 모르겠습니다.

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

저기처럼 디바운싱으로 위치를 저장하시고 리로딩 시에만 스크롤 복원을 하시면 될 것 같습니다. setTimeout을 300 정도 주고난 다음에 window.scrollTo 해보세요.

0

바스니카님의 프로필 이미지
바스니카
질문자

조언해주신 방법대로 useSWRInfinite 이용해서 scrollHeight 문제 해결했습니다. 감사합니다.

또 궁금한 점들이 있습니다.

  useEffect(() => {
    const y = localStorage.getItem("y");
    if (y) window.scrollTo(0, y);
  }, []);

  const scrollPos = useDebouncedCallback(() => {
    localStorage.setItem("y", window.scrollY);
  }, 100);

  useEffect(() => {
    window.addEventListener("scroll", scrollPos);

    return () => window.removeEventListener("scroll", scrollPos);
  }, []);

 

1.

위와 같이 작성하여 previous scroll position을 저장하도록 구현했습니다.

scroll event가 너무 자주 발생해서 debounce를 이용해서 일정 시간 동안은 동작하지 않도록 만들었습니다.

혹시 위 방법 말고 다른 방법으로 previous scroll position을 저장할 수 있는 방법이 있을까요?

 

2.

reload한 뒤에도 previous scroll position이 적용되도록 만들 수 있나요?

 

시도한 방법 1

useEffect(() => {
    const y = localStorage.getItem("y");
    if (y) {
      window.scrollTo(0, y);
    }

    window.onbeforeunload = () => {
      localStorage.setItem("y", window.scrollY);
    };
  }, []);

 

beforeunload 이벤트로 scroll position은 저장이 되는데 해당 위치로 scroll이 이동하진 않습니다.

 

시도한 방법 2

  useEffect(() => {
    const y = localStorage.getItem("y");
    if (y) {
      window.scrollTo(0, y);
    }

    return () => localStorage.setItem("y", window.scrollY);
  }, []);

 

clean up effect를 사용해봤지만 이렇게 하면 window.scrollY가 0이 됩니다.

 

조언 부탁드립니다.

바스니카님의 프로필 이미지
바스니카

작성한 질문수

질문하기