시작하며...
이번 프로젝트에서 TanStack Query 의 useInfiniteQuery 를 사용해서 댓글 무한 스크롤 기능을 구현했다.
그래서 무한 스크롤 기능을 구현하는 것도 처음이고, useInfiniteQuery 를 사용하는 것도 처음이라 댓글 무한 스크롤을 구현한 과정에 대한 내용을 기록하기 위해 글을 작성하게 되었다.
구현하기 앞서...
내가 구현할 댓글 무한 스크롤은 백엔드 서버에서 cursor 값을 사용해서 다음 댓글 데이터를 가져오는 방식이다.
데이터 요청 함수
SSR + TanStack Query
이전 게시물 https://jjang-j.tistory.com/153 에서 다뤘던 내용인데
SSR + TanStack Query 로 데이터 패칭을 요청했다.
무한 스크롤 구현하기
1. useInfiniteQuery 설정하기
1-1. 옵션 설정
- queryFn: pageParam 을 인자로 받아, 해당 cursor 에 해당하는 댓글을 요청하는 역할
- initialPageParam: 첫 번째 페이지로 설정되는 값
- getNextPageParam: 다음 페이지 불러올 때 어떤 값이 필요한지 결정하는 함수
- 서버에서 받아온 마지막 페이지 데이터에서 nextCursor 라는 값으로 다음 페이지를 요청
1-2. 반환 값
- data: 서버에서 받아온 댓글 데이터
- fetchNextPage: 다음 페이지 데이터 불러오는 함수
- hasNextPage: 다음 페이지가 있는지 여부 확인하는 boolean 값
- isFetchingNextPage: 다음 페이지를 가져오는 중인지 확인하는 boolean 값
const {
data: commentListData,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ["board-comment", boardId],
queryFn: ({ pageParam }) => getBoardComment(boardId, pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
2. 스크롤 이벤트 설정하기
무한 스크롤에서 중요한 점은 사용자가 페이지 하단에 도달했을 때, 다음 데이터를 요청하는 것이다.
2-1. 스크롤 이벤트 추가
useEffect 를 사용하여 스크롤 이벤트를 추가한다.
스크롤 이벤트가 실행되면 handleScroll 이라는 함수를 호출한다.
useEffect(() => {
const throttledScrollHandler = () => {
handleScroll();
};
window.addEventListener("scroll", throttledScrollHandler);
return () => {
window.removeEventListener("scroll", throttledScrollHandler);
};
}, [handleScroll]);
2-2. 하단 도달 여부 확인 함수
위에서 호출한 handleScroll 함수이다.
- window.scrollY: 현재 스크롤 위치, 페이지 상단에서부터 얼마나 스크롤했는지
- window.innterHeight: 화면에서 보이는 부분의 높이
- document.body.scrollHeight: 페이지 전체 높이
사용자의 스크롤 위치와 현재 보이는 화면 높이를 더한 값이, 전체 페이지 높이와 거의 같아질 경우가 바로 페이지 끝에 도달했을 때이다.
그래서 이러한 상황에서 다음 페이지가 있고(hasNextPage), 현재 다음 페이지를 요청 중이지 않으면(!isFetchingNextPage) fetchNextPage 를 호출해서 다음 페이지 데이터를 불러오는 방식이다.
const handleScroll = useCallback(() => {
if (
window.scrollY + window.innerHeight >= document.body.scrollHeight - 1 &&
hasNextPage &&
!isFetchingNextPage
) {
fetchNextPage();
}
// 맨 위로 가기 버튼 구현한거라 무시해도 됨
if (window.innerHeight < document.body.scrollHeight) {
setIsVisibleScrollTop(true);
}
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
3. 댓글 렌더링하기
3-1. data 출력해 보기
처음 렌더링된 댓글을 출력하면 다음과 같이 pages 0번 안에 서버에서 받아온 데이터가 들어있는 것을 알 수 있다.
스크롤을 더 내려보면 pages 1번 안에 데이터가 들어있게 된다.
3-2. 코드로 구현하기
페이지 단위로 데이터를 가져오기 때문에, pages 를 맵핑하여 각 페이지 댓글을 가져온다.
추가적으로 다음 페이지를 가져오는 중에는 로딩스피너를 추가하여 사용자의 경험성을 고려하였다.
return (
<section>
{commentListData?.pages[0].list.length ? (
<>
{commentListData.pages.map((page) =>
page.list.map((comment) => (
<Comment
key={comment.id}
commentData={comment}
isEditMode={editMode === comment.id}
setEditMode={setEditMode}
/>
)),
)}
{isFetchingNextPage && (
<div className="loading">
<LoadingSpinner width={30} height={30} />
</div>
)}
</>
) : (
<EmptyComment />
)}
</section>
)
4. 결과
댓글 무한 스크롤이 잘 작동되는 것을 확인할 수 있다.
마치며...
이번 프로젝트에서 그동안 구현해보고 싶었던 기능 중 하나인 무한 스크롤을 실제로 적용할 수 있어 좋았다.
무한 스크롤은 많은 양의 댓글 데이터를 페이징 처리하여 사용자 경험(UX)을 향상할 수 있는 좋은 기능이다. 😊👍
'💜 프로젝트 구현' 카테고리의 다른 글
[TanStack Query] 옵티미스틱 업데이트 사용하여 좋아요 구현 (1) | 2024.09.18 |
---|---|
[Next.js] Lighthouse 웹 사이트 성능 개선 (100으로 만들기) (0) | 2024.09.10 |
[Next.js] 세션 스토리지, 이전 페이지 저장해서 뒤로 가기 구현 (feat. app router) (0) | 2024.09.02 |
[Next.js] 무한 캐러셀 컴포넌트 직접 구현하기 (feat. Tailwind CSS) (2) | 2024.09.02 |
[GitHub Action / Chromatic] 스토리북 PR 미리보기 배포 (feat. yarn) (0) | 2024.08.08 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!