티스토리 뷰
스크롤 탑 버튼 기능을 만들면서 마주했던 가장 큰 문제가 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);
그래서 위와 같이 코드를 작성하게 되면 스크롤탑 버튼 기능이 완성된다. 😉
참고 링크
- https://velog.io/@taeung/React-Custom-Hooks%EB%A1%9C-Scroll-Event-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0
- https://velog.io/@syoung125/Next.js-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-1-Next.js-%EB%9E%80-Next.js%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C-Next.js%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%80
'reactJS' 카테고리의 다른 글
reactJS + springboot 파일 불러오기 및 다운로드 구현하기 (2) (1) | 2022.04.26 |
---|---|
reactJS + springboot 파일 업로드 구현하기 (1) (0) | 2022.04.26 |
typescript 기초 + react 프로젝트 생성 (0) | 2022.02.03 |
reactJS + typescript 프로젝트 만들기 (0) | 2022.01.27 |
open-graph-scraper 사용하기 (0) | 2022.01.26 |
- Total
- Today
- Yesterday
- Hook
- 프로그래머스
- level3
- 이진탐색
- 자바스크립트
- dfs
- React
- springboot
- 이것이 취업을 위한 코딩테스트다
- TypeScript
- axios
- 파이썬
- 소프티어
- 이것이코딩테스트다
- 기초
- CORS
- nomadcoder
- 상태관리
- 면접을 위한 CS 전공지식 노트
- 노마드코더
- level1
- css
- CS
- html
- React.FC
- redux
- JavaScript
- 이코테
- programmers
- reactjs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |