Trouble Shooting

[Facebook chat] React-Query에서 무한스크롤 페이지네이션 구현하기

이 포스트에서는 ToggleDoc에서 페이스북 채팅 기능을 구현하는 과정에서 React-Query를 활용하여 무한 스크롤 페이지네이션을 어떻게 구현했는지에 대한 과정을 공유하고자 한다.

무한 스크롤의 필요성

채팅 기능을 구현하면서 채팅의 모든 내용을 한 번에 불러오게 된다면 로딩 시간이 길어져 UX가 저하될 수 있다. 페이스북, 카카오톡, 슬랙 등 대부분의 대중화된 채팅 서비스의 경우 사용자가 스크롤을 올릴 때마다 일정 범위의 이전 메시지를 불러오는 기능이 포함되어 있다. 채팅 구현에 있어 페이지네이션을 사용하지 않거나 전통적인 페이징 방식(더보기 버튼 등)을 사용하게 된다면 대중적인 채팅 UI와의 간극에 의해 UX의 저하가 발생할 수 있어 ToggleDoc의 채팅 기능에도 무한 스크롤 페이지네이션을 도입하기로 결정했다.

React-Query와 무한 스크롤 페이지네이션

React-Query는 서버 상태 관리를 용이하게 해주는 라이브러리로, 무한 스크롤 구현에 필요한 코드의 양을 줄여주는 useInfiniteQuery 훅을 제공한다. 이 훅을 사용하여 데이터를 끊임없이 로딩하는 방식을 좀 더 간편한 방식으로 구현할 수 있다.

구현 과정

데이터 흐름 설정
  1. 초기 설정: useInfiniteQuery 훅을 사용하여 채팅 메시지를 불러오는 쿼리를 설정한다. 이때, 각 페이지의 마지막 데이터를 기반으로 다음 페이지의 데이터를 요청할 수 있도록 getNextPageParam을 구성한다.
  1. 스크롤 이벤트 감지: 사용자가 스크롤을 맨 위로 올렸을 때 추가 데이터를 불러오도록 이벤트 리스너를 설정한다. 이를 위해 스크롤 위치를 감지하는 로직을 구현한다.
  1. 데이터 불러오기 및 렌더링: 추가 데이터를 불러온 후, 기존의 데이터 리스트에 이어 붙여서 렌더링한다. 이 과정에서 스크롤 위치를 조정하여 사용자가 자연스럽게 이전 대화 내용을 탐색할 수 있도록 한다.
  1. 최적화: 무한 스크롤의 성능을 최적화하기 위해, 불러온 데이터의 양을 적절히 조절하고, 스크롤 이벤트의 호출 빈도를 조절한다.
자연스러운 스크롤 애니메이션 구현
이전 채팅의 데이터가 페칭될 때 유저가 보고 있던 채팅방 내 스크롤의 위치가 임의로 변경된다면 유저가 채팅의 맥락을 다시 파악해야 하는 상황이 발생할 수 있다. 이러한 상황이 발생한다면 좋은 UX라고 하기 어렵다. ToggleDoc은 이러한 문제에 대응하기 위해 useEffect 내에서 이벤트 리스너를 등록 후 이전 채팅 데이터가 페칭-렌더링되는 프레임 전후로 채팅방의 scrollHeight를 비교해 새로 렌더링된 채팅방에서 얼마나 스크롤을 자동으로 내릴지를 판단하는 로직을 사용했다.
useEffect(() => { const messageContainer = document.querySelector('.messages'); (async () => { // 채팅방 스크롤이 최상단에 도달했고, 불러올 이전 대화 내용이 있다면 if (messageContainer.scrollTop === 0 && hasNextPage) { // 페칭 전 채팅방의 높이 const previousHeight = messageContainer.scrollHeight; // 채팅 데이터 페치 await fetchNextPage(); // 부드러운 스크롤을 위한 DOM 업데이트 대기 requestAnimationFrame(() => { // 페칭 후 채팅방의 높이 const currentHeight = messageContainer.scrollHeight; // 추가 데이터 렌더링 전후 채팅방의 높이 차이만큼 스크롤 const newScrollTop = currentHeight - previousHeight; messageContainer.scrollTop = newScrollTop; }); } })(); messageContainer.addEventListener('scroll', handleScroll); return () => messageContainer.removeEventListener('scroll', handleScroll); }, [fetchNextPage, hasNextPage]);

도전과 해결

무한 스크롤 구현 과정에서 스크롤 위치의 정확한 감지, 추가 데이터 로딩 시의 성능 최적화, 그리고 메모리 관리가 주요 도전 과제였다. 이러한 도전을 해결하기 위해 React-Query의 캐싱 기능을 활용하고, useEffect 내 이벤트 리스너를 등록, requestAnimationFrame을 사용하여 스크롤 이벤트의 성능을 개선했다.

결론

React-Query를 사용하여 채팅 내 무한 스크롤 페이지네이션을 구현함으로써, ToggleDoc이 프로덕트 성능 개선과 더불어 사용자에게 더 나은 경험을 제공할 수 있게 되었다. 또한 이러한 접근 방식은 고객, 계약 등 여러 가지 데이터 로딩 전략에도 활용될 수 있으므로 향후 ToggleDoc의 신규 기능이나 리팩토링에서 역시 React-Query를 적극적으로 활용할 수 있는 기반이 마련되었다고 생각한다.