swimminginthecode DIVE!

FRONT/React, Vue

React 07 - react-query로 무한 스크롤 구현하기

dazz6 2024. 12. 11. 18:00

Pageable... 웹 구현에 있어서 목록(리스트) 출력을 해야 하면 피할 수 없는 기능이지만 참 까다롭다...

특히나 여러 필터링이 동시에 진행되어야 할 경우 더욱...

이번에는 모바일 반응형을 위한 무한 스크롤을 구현해 보았다.

달라진 점이 있다면 react-query의 useInfiniteQuery Hook을 사용했다는 점인데,

pageable과 관련된 값들을 전부 별개의 useState로 저장하여 사용했을 때보다 훨씬 간편하다 (아마도)

const {
    data, // 반환된 데이터
    isFetchingNextPage, // isLoading과 같은, 다음 페이지를 불러오고 있는 상태 표시
    hasNextPage, // 마지막 페이지 여부 (다음 페이지가 있는지)
    fetchNextPage, // 다음 페이지를 부르는 함수 
  } = useInfiniteQuery<{ content: IExample[]; last: boolean }, Error>(
    ["example", userId],
    ({ pageParam = 0 }) => PostList(userId, pageParam, 6),
    // pageParam은 useInfiniteQuery가 알아서 관리하는 현재 페이지 상태
    // 6은 size로, 6개씩 정보를 불러오겠다는 것
    {
      getNextPageParam: (lastPage, allPages) => {
      // lastPage : 마지막으로 가져온 페이지의 데이터
      // allPage : 지금까지 가져온 모든 페이지 데이터의 배열
      // getNextPageParam은 useInfinityQuery가 제공하며, 오버라이딩하여 사용하는 것
      // 다음 pageParam 값을 반환
        if (!lastPage.last) {
        // 마지막 페이지가 아니라면, 현재까지 로드된 페이지 개수 반환
          return allPages.length;
        }
        // 마지막 페이지라면, undefined 반환으로 더이상 로드하지 않음
        return undefined;
      },
    }
  );
export const PostList = async (
  userId: number,
  page: number,
  size: number
): Promise<{ content: IExample[]; last: boolean }> => {
// Java의 Map 함수처럼 key:value 형태로 반환할 것
    const response = await axios.get<{
    
      // 서버로부터 받아오는 데이터의 형식 지정
      content: IExample[];
      
      // Page 객체를 반환하여 사용하므로 타입 지정
      totalPages: number;
      number: number;
      
    }>(`/api/users/${userId}/example`, {
      params: {
        page,
        size,
      },
    });

	// 반환받은 데이터의 data 저장
    const data = response.data;

	// data의 content, 마지막 페이지 여부 반환
    return {
      content: data.content,
      last: data.number + 1 >= data.totalPages,
    };
};

이렇게 useInfiniteQuery를 사용한 서버와의 통신 부분은 끝이다. 

하지만 무한스크롤을 구현하기 위해서는 스크롤을 내리는 이벤트에 반응하여

알아서 정보를 불러와야 하기에 useEffect를 걸어 준다.

useEffect(() => {
    const onScroll = () => {
      if (
        window.innerHeight + window.scrollY >=
          document.body.offsetHeight - 500 &&
        !isFetchingNextPage &&
        hasNextPage
      ) {
        fetchNextPage();
      }
    };
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, [isFetchingNextPage, hasNextPage, fetchNextPage]);

끝~

data?.pages
	.flatMap((page) => page.content)
	.map((sdata) => (...)

jsx 부분에서 이렇게 출력하면 된다.

flatMap은 page 단위로 나뉜 자료들을 하나로 합쳐 주고, 거기서 content만 추출한다.

마지막 페이지 여부 반환값은 데이터를 불러와야 하는지의 여부를 파악하기 위한 것이지 출력에는 관여하지 않기 때문이다.

그리고 하던 방식 대로 map을 사용해서 반복 출력한다.

 

처음 써 보는 방식이라 낯설었지만 흥미로운 방식! 그리고 react-query는 정말 멋진 라이브러리인 것 같다... ㅜㅜ

'FRONT > React, Vue' 카테고리의 다른 글

React 09 - 커스텀 훅과 HOC (고차 컴포넌트)  (0) 2025.05.07
React 08 - react-copy-to-clipboard 사용해 보기  (0) 2024.12.30
React 06 - react-toastify 사용해 보기  (0) 2024.12.10
Vue 01  (0) 2024.08.14
React 05  (0) 2024.08.13