티스토리 뷰

reactJS

react + nextjs 스크롤탑 버튼 만들기

코딩하는 둥아 2022. 7. 12. 15:56
728x90

스크롤 탑 버튼 기능을 만들면서 마주했던 가장 큰 문제가  NextJS Document is not defined 였다.

위의 문제를 해결하려면 nextJS의 특징을 알아야 한다.

nextJS란?

nextJS는 React 라이브러리의 프레임워크이다.

가장 큰 이유는 pre-rendering을 통해 SEO(Search Engine Optimization)와 SSG(Static-Site-Generate)을 위한 Server-Side Rendering을 가능하기 때문에 사용한다.

기본 리액트는 Client Side Rendering이므로 웹사이트를 요청했을 때 빈 html을 가져와 script를 로딩하여 첫 로딩시간이 오래걸리고, SEO에 취약하다는 단점이 있다.

NextJS를 사용하면 pre-reloading을 제공하여 미리 데이터가 렌더링 된 페이지를 가져올수 있게 해주므로 SEO에서도 장점을 가질 수 있다. 또한 페이지 기반 라우팅 시스템을 제공한다.

 

오류 해결 방법

nextJS는 CSR로 이루어진 리액트를 서버사이드 렌더링이 가능하게 해준다. 

server-side에서 먼저 실행되고, 그 후에 client-side에서 실행이 된다. 그런데 window나 document 객체는 client-side에만 존재하기 때문에 위의 에러가 발생하게 된다.

그래서 이를 해결하기 위해서는 컴포넌트가 모두 마운트 된 후에 window나 document 객체를 사용해야 한다.

 

그래서 TopButton Component를 호출하는 MyContent 컴포넌트가 마운트 되었을 때 store에 저장된 mount 변수의 값을 업데이트 시켜주고, mount가 true일 때 스크롤을 하고자 하는 div에 scroll 이벤트를 걸어주었다. 함수형 컴포넌트를 사용하기 때문에 useEffect에 dependency를 []로 넣어주어 ComponentDidMount와 같은 동작이 일어나도록 하였다.

 

포인트는 내가 스크롤을 걸 컴포넌트의 마운트 여부를 확인해야 한다는 점이다.

이것 떄문에 삽질을 조금 오래했다. 버튼 컴포넌트의 마운트 여부가 아니라 스크롤 대상의 마운트 여부!!!!

 

useEffect(() => {
        if(store.mounted) {
            document.getElementsByClassName('container-content')[0].addEventListener('scroll', debounce(listener, 15));
            return () => document.getElementsByClassName('container-content')[0].removeEventListener('scroll', listener);
        }
}, [store, store.mounted]);

또한 위처럼 scroll 이벤트를 걸어줄 때, 꼭 unmount시 이벤트를 해지해줘야 한다.

import { observer } from 'mobx-react-lite';
import React, { useState, useEffect } from 'react';
import useMyStore from "@stores/global/my";
import debounce from 'lodash/debounce';

const TopButton = () => {
    const store = useMyStore();
    const [scrollY, setScrollY] = useState<number>(0);

    const listener = () => {
        setScrollY(document.getElementsByClassName('container-content')[0].scrollTop);
    };

    const scrollTop = () => {
        document.getElementsByClassName('container-content')[0].scrollTo(0,0);
    }

    useEffect(() => {
        if(store.mounted) {
            document.getElementsByClassName('container-content')[0].addEventListener('scroll', debounce(listener, 15));
            return () => document.getElementsByClassName('container-content')[0].removeEventListener('scroll', listener);
        }
    }, [store, store.mounted]);

    return (
        scrollY > 0 ? (
            <div className="fixed-top">
                <button type="button" className="btn-top" onClick={scrollTop}>
                    <span />
                </button>
            </div>
        ):
        <></>
    );
};

export default observer(TopButton);

 

const MyContents = () => {
  const store = useGlobalStore();
  const myStore = useMyStore();
  const loading = useRef<boolean>(false);

  useEffect(() => {
    myStore.setMounted(true);
  }, []);

  return (
      <>
        ...
        <TopButton />
      </>
  );
};

export default observer(MyContents);

그래서 위와 같이 코드를 작성하게 되면 스크롤탑 버튼 기능이 완성된다. 😉

 

참고 링크

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함