들어가며
next.js에서 react-query의 useInfiniteQuery를 이용하여 무한 스크롤 만들기 이 글에서는 useInfiniteQuery를 이용한 무한 스크롤 구현 주제를 중심으로 내용을 정리했습니다. 검색이나 북마크로 다시 찾아올 때 빠르게 맥락을 잡을 수 있도록 서두와 마무리를 덧붙였습니다.
아래 본문은 기존에 작성해 두었던 내용을 유지하며, 구조만 블로그 글 형태에 맞게 다듬었습니다.
무한 스크롤이란?
리스트에 필요한 데이터를 한번에 요청하여 보여주는 것이 아닌, 사용자가 스크롤을 내릴 때 마다 페이지네이션 데이터를 요청하여 기존 리스트에 새로운 데이터를 붙여서 사용자에게 보여주는 방법입니다.
useInfiniteQuery
react-query의 useInfiniteQuery 훅을 사용하여 깔끔하게 무한스크롤에 필요한 데이터 상태를 관리할 수 있습니다.
useInfiniteQuery 레시피
- useQuery와 동일하게 queryKey와 queryFn을 정의합니다
- getNextPageParam에 다음 페이지를 불러오기 위한 params를 반환하는 함수를 정의합니다.
이 함수의 첫번째 파라미터는 가장 최근에 불러온 마지막 페이지 데이터를, 두번째 파라미터는 모든 페이지 데이터를 받을 수 있습니다. - useInfiniteQuery는 queryFn을 getNextPageParam과 함께 call하는 fetchNextPage 함수를 반환합니다. 이 함수를 호출해서 다음 페이지를 fetch할 수 있습니다.
- react-intersection-observer 라이브러리에서 제공하는 useInView라는 훅을 통해서 엘리먼트가 view 안에 들어왔는지에 대한 상태인 inView 상태를 활용할 수 있습니다.
- inView=true 상태가 되었을 때 fetchNextPage를 호출하는 useEffect를 만듭니다.
전체 소스코드
const { ref, inView } = useInView(); const { data, fetchNextPage } = useInfiniteQuery( ["items"], async ({ pageParam }) => { return await fetch("/items", { ...pageParam }); }, { getNextPageParam: (lastPage, allPages) => { if (!lastPage.length) return; return { page: allPages.length + 1, size: pageSize, }; }, }, ); useEffect(() => { if (inView) { fetchNextPage(); } }, [fetchNextPage, inView]); return ( <> <ItemList items={data.pages.flat()} /> <div ref={ref} /> </> );
참고자료
react-query - useInfiniteQuery
react-query - example
mdn - Intersection Observer
github - react-intersection-observer
마무리
- 이 글의 예제와 설명은 작성 시점 기준이므로, 지금 사용 중인 런타임·프레임워크·라이브러리 버전과 맞는지 공식 문서로 한 번 더 확인하는 것이 좋습니다.
- 예제 코드는 이해를 돕기 위한 것이니, 실서비스에 적용할 때는 에러 처리·경계 조건·보안(CORS, 토큰, 입력 검증 등) 을 팀 규칙에 맞게 보강하세요.
- 같은 주제로 정리한 글이 있다면 서로 내부 링크를 걸어 두면 나중에 복습하기 편합니다.