패스트캠퍼스 데브캠프

파이널 프로젝트 - 내 프로젝트 (7) - Tanstack-Query useInfinity

vitamin3000 2025. 4. 4. 12:28

 

이번  포스트에서는 Tanstack-Query의 useInfinityQuery hook사용에 대해 작성해보고자 한다.

 

먼저, 구현된 화면 UI는 다음과 같다.

 

프로젝트 아이템을 한 화면에서 9개까지 보여주고 있음을 확인할 수 있다.

 

만약 100개의 프로젝트 아이템이 있다고 가정해보자.

한 화면에 나올 아이템은 9개인데, 실제 저장된 데이터는 100개이면, 로딩 및 랜더링에 시간이 더 오래 걸릴 것이다.

 

따라서, 100개의 데이터중 9개만 우선적으로 갖고오고, 나머지는 useInfinity hook을 사용하여 9개씩 불러오도록 하였다.

 

먼저 사용자의 프로젝트 아이템을 불러오는 api 코드는 다음과 같다.

 

import api from '@/api/login/api';
import { getTokens } from '@/utils/Tokens';

export type videoArchiveType = {
  id: number;
  projectId: number;
  jobId: string;
  type: string;
  status: string;
  retryCount: number;
  createdAt: string;
  updatedAt: string;
};

export const getVideoArchive = async () => {
  const { accessToken } = getTokens();
  try {
    const response = await api.get('/api/v1/tasks', {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }
};

 

위 코드에서는 9개 처리에 대한 코드가 구현되어 있지 않다.

 

useInfinity hook에 대한 값을 설정할 때 작성한다.

사용법은 https://vitamin3000.tistory.com/207를 참조하기 바란다.

 

TanStackQuery (11) - 페이지별, 무한 스크롤 쿼리 적용

이번 포스트에서는 지난 포스트에서 학습한 페이지별, 무한 스크롤을 실제로 적용해보고자 한다. QueryClientProvider로 최상위 컴포넌트를 감싸야 한다.왜? -> 이것을 통해 React Query의 전역 상태를

vitamin3000.tistory.com

 

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isLoading,
    error,
    refetch,
  } = useInfiniteQuery({
    queryKey: ['projects', activeTab],
    queryFn: ({ pageParam = 0 }) => getProjects(pageParam, 9),
    getNextPageParam: (lastPage, allPages) => {
      const nextOffset = allPages.length * 9;
      return lastPage.hasMore ? nextOffset : undefined;
    },
    initialPageParam: 0,
  });

  useEffect(() => {
    refetch();
  }, [activeTab, refetch]);

  const handleObserver = useCallback(
    (entries: IntersectionObserverEntry[]) => {
      const [entry] = entries;
      if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) {
        fetchNextPage();
      }
    },
    [fetchNextPage, hasNextPage, isFetchingNextPage]
  );

  useEffect(() => {
    const observer = new IntersectionObserver(handleObserver, {
      threshold: 0.1,
    });
    const currentTarget = observerTarget.current;

    if (currentTarget) {
      observer.observe(currentTarget);
    }

    return () => {
      if (currentTarget) {
        observer.unobserve(currentTarget);
      }
    };
  }, [handleObserver]);

 

 

이때 로딩 스피너는 다음과 같이 구현하였다.

 

type LoadingSpinnerProps = {
  text: string;
  showText: boolean;
}

const LoadingSpinner = ({
  text = '로딩 중...',
  showText = true,
}: LoadingSpinnerProps) => {
  return (
    <Container>
      <SpinnerWrapper />
      {showText && <Text>{text}</Text>}
    </Container>
  );
};

 

작성한 애니메이션은  다음과 같다. 뱅글뱅글 돈다

// 회전 애니메이션 정의
const spin = keyframes`
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
`;