채팅 - 무한스크롤 오류

생성일
2025/01/31 06:45
FE
날짜
2024/12/21
담당자

문제 상황

채팅방 첫 입장 시 가장 최신의 메시지만 보이고 더 이전의 메시지는 무한스크롤을 통해 로드해야 하는데, 이전 메시지가 모두 한번에 로드됨

원인

useIntersectionObserver의 ref 요소 위치
ref 요소가 메시지 목록 아래에 있어, 채팅방에 처음 들어오면 바로 ref 요소가 뷰포트에 들어오게 됨  그 즉시 모든 페이지가 연속으로 로드됨
잘못된 이전 메시지 로드 및 스크롤 관리
이전 메시지 로드와 스크롤 위치 조정을 하나의 함수에서 동시에 처리  DOM 업데이트가 완료되기 전에 스크롤 위치가 조정될 수 있음

해결

useIntersectionObserver의 ref 요소 위치 변경
ref 요소를 메시지 목록 위에 위치하도록 수정하여, 사용자가 스크롤을 위로 올려서 이전 메시지를 보고자 할 때 이전 메시지가 로드됨
return ( <div ref={chatContainerRef} className="flex-1 h-[500px] overflow-y-auto space-y-4 px-6 pb-6"> {hasNextPage && !isFetchingNextPage && ( <div ref={ref} className="h-1" /> // ref 요소가 메시지들 위에 위치 )} {messages.map((msg, idx) => { // ... 메시지 렌더링 })} </div> );
TypeScript
복사
이전 메시지 로드 함수와 (초기 로딩, 이전 메시지 로드, 새 메시지 추가) 3가지의 상황을 분리하여 스크롤 처리
이전 메시지 로드 함수
const handleFetchPreviousMessages = async () => { const previousScrollHeight = chatContainerRef.current.scrollHeight; const previousScrollTop = chatContainerRef.current.scrollTop; await fetchNextPage(); chatContainerRef.current.scrollTop = newScrollHeight - previousScrollHeight + previousScrollTop; };
TypeScript
복사
스크롤 처리
초기 로딩 시: 스크롤을 맨 아래로 이동
useEffect(() => { if (messages.length && chatContainerRef.current && isInitialLoad.current) { chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; lastMessageLength.current = messages.length; isInitialLoad.current = false; } }, [messages]);
TypeScript
복사
이전 메시지 로드 시: DOM 업데이트 후 스크롤 위치 조정 / 이전 메시지 개수 기준으로 적절한 스크롤 위치 계산
useEffect(() => { if (!isInitialLoad.current && isLoadingPrevMessages.current && chatContainerRef.current && messages.length !== lastMessageLength.current) { const newScrollHeight = chatContainerRef.current.scrollHeight; const targetScrollTop = newScrollHeight - lastMessageLength.current * 100; chatContainerRef.current.scrollTop = targetScrollTop; lastMessageLength.current = messages.length; isLoadingPrevMessages.current = false; } }, [messages]);
TypeScript
복사
새 메시지 추가 시: 스크롤을 맨 아래로 이동 / isLoadingPrevMessages 체크를 통해 이전 메시지 로드와 구분
useEffect(() => { if (!isInitialLoad.current && !isLoadingPrevMessages.current && chatContainerRef.current && messages.length > lastMessageLength.current) { chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight; lastMessageLength.current = messages.length; } }, [messages]);
TypeScript
복사
이전 메시지를 읽는 중에도 새 메시지가 추가되면 스크롤이 자동으로 이동하여 사용자가 불편할 수 있다고 느껴 개선할 예정