[Main Project] UDog / 구현하기 - 무한 스크롤 구현하기

soohyunee·2023년 3월 13일
0

[Main Project] UDog

목록 보기
6/18

1. 구현하기

진행 상황

  • AWS S3로 클라이언트 1차 배포
  • 내주변 미용실 페이지 무한 스크롤 적용

진행 예정

  • 미용실 상세 페이지

2. TIL

2-1. 무한 스크롤 구현하기

scroll 이벤트 활용하기

내주변 미용실 페이지와 스타일북 페이지에서 컨텐츠들이 끊임없이 보여지게끔 무한 스크롤을 구현하기로 했다. 우선 무한 스크롤에 대해 알아보니 오프셋 기반과 커서 기반 두 가지 방법으로 구현이 가능한 것을 알게 되었다.
오프셋 기반 무한 스크롤은 일반적인 방법이지만 무한대로 스크롤이 가능하고 서버의 성능 저하 우려 문제가 있고, 커서 기반 무한 스크롤은 서버에서 보낸 마지막 데이터 포인터를 저장하고, 페이지에서 다음 데이터를 요청할 때 이 포인터를 사용하여 데이터 무결성과 성능, 부하 분산, 사용자 경험 개선 등 오프셋 기반에 비해 좋은 점이 있다.
그래서 우리는 커서 기반 무한 스크롤을 구현하고자 했고, 커서 기반 무한 스크롤을 구현하려면 응답 헤더의 cursor값이 필요한데 그 부분이 json server로 테스트가 가능할지 아직 모르겠어서 api가 완성되는 동안 우선 오프셋 기반으로 구현을 시작했다.

시도한 방법

clientHeight는 target의 높이, scrollTop는 스크롤 한 높이, scrollHeight는 마진 제거한 전체 스크롤 높이를 뜻한다. 스크롤 이벤트가 발생했을 때 event.target으로 접근하여 해당 값들을 얻을 수 있다. 그래서 가장 바닥으로 내려왔을때 page 상태 변경 함수를 실행한다.

	const handleScroll = (e) => {
		const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
		if (bottom && !loading && hasMore) {
			setPage((prevPage) => prevPage + 1);
		}
	};

useEffect로 page가 변경될 때마다 axios get 요청을 보내게끔 작성하고, res로 받아온 data를 data 상태 변경함수 안에 넣어준다. 이때 spread를 이용하여 기존 데이터와 새로운 데이터를 병합한다. 리액트는 불변성을 유지해야하기 때문에 기존 배열에 작업하지 않고 항상 새로운 배열에 작업을 해야한다.

useEffect(() => {
	setLoading(true);
	API.get(`${url}_page=${page}&_limit=${perPage}`)
		.then((res) => {
		setData((prevData) => [...prevData,...res.data]);
		setHasMore(res.data.length > 0);
		})
		.finally(() => setLoading(false));
}, [page]);

얼추 기능이 구현됐다고 생각하고 콘솔창을 열어봤는데 에러는 아니지만 위치를 받아오는 컴포넌트가 스크롤을 내릴 때 마다 같이 렌더링이 되는 것을 확인할 수 있었다.

해결 방법

해당 컴포넌트는 페이지 렌더링시 한번만 실행되면 컴포넌트여서 memo를 사용하여 값을 기억하게 해서 렌더링이 일어나지 않게 막아주었다.

export default memo(Location);

2-2. 상단 이동 버튼 구현하기

useRef 사용하여 접근

무한 스크롤이 무사히 잘 구현되나 싶었는데 팀원 분이 만드신 탑 버튼 적용되지 않음을 뒤늦게 알게되었다. 스크롤 영역에 스크롤 이벤트 핸들러를 연결해주었고, 해당 스크롤 영역은 height:100vh, overflow-y: auto로 스타일을 지정해주었다.
그래서 스크롤 영역의 문제인가 싶어서 height를 100%으로 주니까 top버튼은 되는데 무한스크롤이 안되는 상황이 발생했다.

시도한 방법

팀원분이 작성한 파일을 보니 버튼 클릭 이벤트 핸들러 함수에window.scrollTo로 top:0으로 이동시키는 로직을 작성하여 상단 이동 버튼을 구현하셨다. 그래서 해당 함수 안에 콘솔로그로 아무 데이터를 넣어서 버튼을 클릭해보니 콘솔창에는 값이 찍히지 않았다.
그래서 window 객체가 아닌 스크롤 영역 컴포넌트로 dom을 참조하여 해당 영역에 scrollTo를 실행시키는 로직을 작성하였다.

const scrollAreaRef = useRef(null);

<S.ScrollArea ref={scrollAreaRef} onScroll={handleScrollEvent}>
<ScrollTopButton area={scrollAreaRef}

ref로 받아온 값을 버튼에 props로 내려주어 scrollTo를 실행시켜주었지만 에러가 발생했다.

해결 방법

area 변수는 useRef를 사용하여 생성된 Ref 객체를 참조하는데 Ref 객체가 참조하는 실제 DOM 요소를 가져오려면 .current 속성을 사용해야한다.
area 변수만 사용하여 DOM 요소에 접근하려고 하면, Ref 객체를 직접 참조하는 것이 아니기 때문에 undefined 값이 반환어서 not a function 에러가 발생하는 것이다.

const handleClick = () => {
	if (area.current) {
		area.current.scrollTo({
			top: 0,
			behavior: 'smooth',
		});
	}
};
profile
FrontEnd Developer

0개의 댓글