대량 데이터로 무한 스크롤 구현 시 고려할 사항

eunbi·2025년 2월 2일

CS 총정리

목록 보기
13/22

무한 스크롤(Infinite Scroll)은 사용자가 특정 버튼을 클릭하지 않아도 스크롤을 내릴 때 새로운 데이터를 불러오는 기법이다.

10,000개 이상의 대량 데이터를 다룰 경우, 성능 최적화렌더링 최적화가 매우 중요하다.

이 글에서는 대량 데이터 무한 스크롤을 구현할 때 고려해야 할 핵심 요소를 정리한다.


1. 성능 최적화 (Pagination 활용)

📌 한 번에 모든 데이터를 렌더링하면 성능 저하 발생

  • 데이터를 한 번에 모두 불러오면 브라우저 메모리 사용량 증가
  • 렌더링 성능 저하 및 UI 지연 현상 발생

해결 방법: Pagination (페이지네이션) 사용

  • 데이터를 일정 단위(예: 20~50개)씩 가져오기
  • 서버에서 데이터 요청 시 limitoffset을 설정하여 필요한 데이터만 가져옴

💡 React Query의 useInfiniteQuery를 사용하면 효율적인 데이터 페이징이 가능

import { useInfiniteQuery } from "@tanstack/react-query";
import axios from "axios";

const fetchPosts = async ({ pageParam = 1 }) => {
  const res = await axios.get(`/api/posts?limit=20&page=${pageParam}`);
  return res.data;
};

const InfiniteScrollComponent = () => {
  const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage, pages) => lastPage.nextPage,
  });

  return (
    <div>
      {data?.pages.map((group, i) =>
        group.posts.map((post) => <div key={post.id}>{post.title}</div>)
      )}
      {hasNextPage && <button onClick={() => fetchNextPage()}>더 보기</button>}
    </div>
  );
};

✔️ useInfiniteQuery는 데이터를 페이지 단위로 불러오면서 필요한 경우에만 요청을 수행

✔️ getNextPageParam을 사용하여 다음 페이지의 존재 여부를 판단


2. 가상화(Virtualization) 적용

📌 모든 데이터를 DOM에 렌더링하면 성능 문제가 발생

  • DOM 요소가 많아질수록 브라우저의 렌더링 속도 저하
  • 10,000개의 데이터를 한꺼번에 렌더링하면 프레임 드롭(Frame Drop) 발생

해결 방법: React Virtualization 사용

  • 화면에 보이는 요소만 렌더링하고, 보이지 않는 요소는 제거
  • 대표적인 라이브러리: react-window, react-virtualized

💡 react-window를 사용한 리스트 가상화 예제

import { FixedSizeList as List } from "react-window";

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const VirtualizedList = () => (
  <List height={500} itemCount={10000} itemSize={35} width={"100%"}>
    {Row}
  </List>
);

export default VirtualizedList;

✔️ 항목이 10,000개여도 실제로 렌더링되는 항목은 화면에 보이는 개수만큼 제한

✔️ itemSize로 각 항목의 높이를 지정하여 최적화


3. 스크롤 이벤트 최적화 (Intersection Observer 사용)

📌 스크롤 이벤트를 직접 감지하면 성능 문제가 발생

  • onscroll 이벤트를 직접 사용하면 스크롤할 때마다 계속해서 이벤트가 실행
  • 불필요한 연산이 많아지면서 성능 저하

해결 방법: Intersection Observer API 활용

  • 특정 요소가 화면에 나타날 때만 이벤트를 트리거
  • 효율적으로 데이터를 로드하면서 성능 최적화

💡 Intersection Observer를 활용한 무한 스크롤 예제

import { useEffect, useRef } from "react";
import { useInfiniteQuery } from "@tanstack/react-query";
import axios from "axios";

const fetchPosts = async ({ pageParam = 1 }) => {
  const res = await axios.get(`/api/posts?limit=20&page=${pageParam}`);
  return res.data;
};

const InfiniteScrollComponent = () => {
  const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage) => lastPage.nextPage,
  });

  const observerRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasNextPage) {
          fetchNextPage();
        }
      },
      { threshold: 1.0 }
    );

    if (observerRef.current) observer.observe(observerRef.current);

    return () => {
      if (observerRef.current) observer.unobserve(observerRef.current);
    };
  }, [fetchNextPage, hasNextPage]);

  return (
    <div>
      {data?.pages.map((group) =>
        group.posts.map((post) => <div key={post.id}>{post.title}</div>)
      )}
      <div ref={observerRef} />
    </div>
  );
};

✔️ IntersectionObserver를 사용하여 특정 요소(observerRef)가 화면에 나타날 때 fetchNextPage() 실행

✔️ 불필요한 스크롤 이벤트 핸들링을 방지하여 성능 최적화


4. 데이터 캐싱과 로딩 상태 관리

📌 데이터 요청을 최적화하여 네트워크 트래픽 감소

  • 이미 가져온 데이터는 다시 요청하지 않도록 캐싱 필요

해결 방법: React Query의 데이터 캐싱 활용

  • staleTime을 설정하여 일정 시간 동안 새 요청을 방지
  • isFetching 상태를 활용해 로딩 상태를 표시

💡 React Query의 데이터 캐싱 설정

const { data, isFetching } = useInfiniteQuery({
  queryKey: ["posts"],
  queryFn: fetchPosts,
  staleTime: 60000, // 60초 동안 캐싱 유지
});

✔️ staleTime: 60000 설정 시, 60초 동안 동일한 데이터를 다시 요청하지 않음

✔️ isFetching을 활용하여 로딩 UI 추가 가능


5. 무한 스크롤 구현 시 고려해야 할 추가 사항

1) 스켈레톤 UI 적용

  • 데이터가 로딩 중일 때 스켈레톤 UI를 표시하여 사용자 경험 개선
  • React Query의 isFetching을 활용하여 구현 가능

2) API 요청 한계 처리

  • 데이터 요청이 많아지면 서버 부하가 발생할 수 있음
  • debounce 또는 throttle을 활용하여 요청 빈도 조절 가능

3) 사용자의 네트워크 상태 고려

  • 느린 네트워크 환경에서는 무한 스크롤이 불편할 수 있음
  • "더 보기" 버튼을 추가하여 대체 UI 제공 가능

🔹 결론

대량 데이터를 활용한 무한 스크롤을 구현할 때는 렌더링 성능과 데이터 요청 최적화가 핵심이다.

✔️ Pagination을 활용하여 적절한 양의 데이터만 로드

✔️ Virtualization을 적용하여 화면에 보이는 요소만 렌더링

✔️ Intersection Observer를 활용하여 불필요한 스크롤 이벤트 방지

✔️ React Query를 사용하여 데이터 캐싱 및 네트워크 요청 최적화

0개의 댓글