무한 스크롤
무한 스크롤이란 데이터를 끊김없이 제공하는 방식이다.
페이지네이션도 많은 양의 데이터를 보여줄 때 효과적이지만, 모바일 환경 같은 경우에는 끊김없이 데이터가 연속적으로 이어지는 것이 조금 더 나은 사용자 경험에 기여할 수 있다. 페이지네이션은 버튼을 눌러 페이지를 이동해 새로운 데이터를 보여준다면, 무한 스크롤을 사용하면 페이지 이동 없이 데이터를 연속적으로 보여줄 수 있기 때문이다.
무한 스크롤이라고 해서 모든 데이터를 한꺼번에 가져오는 것은 아니다. 페이지네이션과 마찬가지로 일정량의 데이터를 fetch하고, 화면의 끝에 다다르면 새 데이터를 fetch한다.
이번 프로젝트에 무한 스크롤을 구현하면서 공부한 것을 정리해보고자 한다.
리액트에서 무한 스크롤을 구현하는 방법들
- scroll event
사용자의 스크롤을 감지하여 페이지 끝에 다다르면 새롭게 데이터를 불러온다.
- 컴포넌트를 참조하여 스크롤 이벤트를 감지하다가
- 컴포넌트의 scrollHeight(스크롤 할 수 있는 최대 화면 높이) == offsetHeight(컴포넌트 높이, border까지 포함) + scrollTop(화면 최상단에서 얼마나 scroll 되어 있는지) 이 되면 새롭게 데이터를 불러온다.
- 방식 자체는 간단하지만 scroll event 자체를 throttle 같은 방식으로 최적화해줘야 한다. (throttle 외에 requestAnimationFrame을 이용해서 최적화할 수도 있다고 한다.)
참고 이미지들



- react-infinite-scroll-component 라이브러리
무작정 라이브러리로 해결하는 것보다 다른 방식으로 해보면서 배운 뒤에 사용하는 것이 나을 것 같아서 이번에는 pass. - IntersectionObserver API
내가 이번 프로젝트에서 무한 스크롤을 구현한 방식!
IntersectionObserver API
설정한 타겟요소(관찰대상)와 뷰포트의 교차여부를 비동기적으로 관찰(observe) 하는 API. Docs
사용 방법
// 1. IntersectionObserver를 생성하며 콜백함수를 넘긴다.
const observer = new IntersectionObserver(observerCallback);
// 2. 생성된 observer가 타겟요소를 관찰하도록 한다.
observer.observe(target);
끝! 너무나 간단하다. 무한스크롤을 구현하려면 콜백함수를 적절히 넘기기만 하면 될 것이다.
const observerCallback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && hasNextPage) {
fetchNextPage();
}
});
};
나는 @tanstack/react-query의 useInfiniteQuery를 이용했기 때문에 코드가 간결하지만 직접 구현하려면 더 복잡할 것이다.
아무튼, entries는 배열로, 타겟 하나 당 하나의 엔트리에 해당한다고 보면 될 듯하다.
isIntersecting은 교차 여부이다. 그래서 배열 각각의 요소(타겟)에 대해서 isIntersecting 교차되면 -> 데이터를 가져와라! 는 구조인 것이다.
다만 다음 페이지가 있을 때만 다음 페이지를 불러오도록 한다. (useInfiniteQuery야 고마워)
설정할 수 있는 옵션들
페이지 가장 밑에 도달했을 때 콜백함수를 부르는 게 일반적이겠지만(그래서 나는 옵션을 생략했다).
페이지 70퍼센트에 도달했을 때, 80퍼센트에 도달했을 때 등 세밀하게 그 수치를 조절할 필요가 있다면 옵션을 주면 된다.
root : 관찰자 라고 볼 수 있다. default는 null 인데 이 경우 브라우저의 viewport가 사용된다.rootMargin : 바깥의 여백을 설정해서 범위 수정 가능.threshold : 교차 정도. 0 이면 타겟이 아직 뷰포트에서 보이지 않을 것이고 80퍼센트면 80퍼센트가 교차하고 있을 것이다. 0부터 1까지의 숫자를 넣을 수 있고, 배열도 넣어서 여러번 실행되도록 할 수도 있다.
그 외의 자세한 내용은 잘 정리된 블로그가 있어서 링크로 대체한다. 필요할 때 가서 참고하면 좋을 듯하다!
참고
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
https://heropy.blog/2019/10/27/intersection-observer/
'Frontend > React' 카테고리의 다른 글
| 리액트+vite 프로젝트에서 번들 사이즈 줄이기 (0) | 2024.03.01 |
|---|---|
| [React] react-hook-form 과 zod로 리액트 form 리팩토링하기 (1) | 2024.02.13 |
| [React.dev] 2. UI를 트리구조로 이해하기 (0) | 2023.12.03 |
| [React.dev] 1. 컴포넌트를 순수하게 (0) | 2023.12.01 |
| [React.dev] 리액트 공식문서 읽으면서 공부하기 (0) | 2023.11.27 |