React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

1. 알고리즘 소개 및 개념

React에서 가상 스크롤링 구현하기는 대용량 리스트를 효율적으로 렌더링하기 위한 핵심 최적화 기법입니다. 수천, 수만 개의 데이터를 화면에 표시할 때 모든 항목을 DOM에 렌더링하면 성능이 급격히 저하됩니다. 가상 스크롤링(Virtual Scrolling)은 현재 뷰포트에 보이는 항목들만 실제로 렌더링하고, 나머지는 메모리에만 유지하는 기술입니다.

이 알고리즘의 핵심 아이디어는 “보이지 않는 것은 렌더링하지 않는다”입니다. 사용자가 스크롤할 때 동적으로 보이는 영역을 계산하고, 해당 영역의 아이템만 DOM에 마운트합니다. 예를 들어, 10,000개의 아이템이 있어도 화면에는 20-30개만 표시되므로, 실제로는 20-30개의 DOM 노드만 생성됩니다. 이를 통해 메모리 사용량을 줄이고 렌더링 성능을 극적으로 향상시킬 수 있습니다.

2. 동작 원리 상세 설명

가상 스크롤링의 동작 원리는 크게 세 가지 핵심 요소로 구성됩니다.

첫째, 뷰포트 계산입니다. 현재 스크롤 위치(scrollTop)와 컨테이너 높이(clientHeight)를 기반으로 화면에 보이는 영역을 계산합니다. 예를 들어, 스크롤 위치가 500px이고 각 아이템 높이가 50px이라면, 10번째 아이템부터 보이기 시작합니다.

둘째, 가시 범위 인덱스 계산입니다. startIndex = Math.floor(scrollTop / itemHeight) 공식으로 첫 번째 보이는 아이템을 찾고, endIndex = startIndex + visibleCount로 마지막 보이는 아이템을 계산합니다. 성능 향상을 위해 버퍼(buffer)를 추가하여 스크롤 시 깜빡임을 방지합니다.

셋째, 오프셋 조정입니다. 실제로는 일부 아이템만 렌더링하지만, 전체 리스트의 스크롤바 크기는 유지해야 합니다. 이를 위해 컨테이너의 총 높이를 totalHeight = itemCount × itemHeight로 설정하고, 렌더링된 아이템들을 offsetY = startIndex × itemHeight만큼 아래로 이동시켜 정확한 위치에 배치합니다. 이렇게 하면 사용자는 전체 리스트를 스크롤하는 것처럼 느끼지만, 실제로는 보이는 부분만 렌더링되는 것입니다.

3. 시간/공간 복잡도 분석

시간 복잡도

  • 초기 렌더링: O(v) – v는 뷰포트에 보이는 아이템 수
  • 스크롤 이벤트 처리: O(1) – 인덱스 계산만 수행
  • 리렌더링: O(v) – 항상 고정된 수의 아이템만 렌더링
  • 전통적 렌더링 O(n)과 비교하면 n >> v이므로 엄청난 성능 향상

공간 복잡도

  • DOM 노드: O(v) – 보이는 아이템만 DOM에 존재
  • 데이터 저장: O(n) – 전체 데이터는 메모리에 유지
  • 전통적 방식은 O(n)개의 DOM 노드를 생성하지만, 가상 스크롤링은 O(v)로 제한

예를 들어, 10,000개 아이템 중 20개만 보인다면, 전통적 방식은 10,000개의 DOM 노드를 생성하지만 가상 스크롤링은 20-30개만 생성합니다. 이는 500배의 DOM 노드 감소를 의미하며, 초기 렌더링 시간을 수 초에서 밀리초 단위로 단축시킵니다.

4. 단계별 구현 과정

Step 1: 기본 구조 설정

먼저 필요한 상태값들을 정의합니다.

import React, { useState, useRef, useEffect } from 'react';

const VirtualScroll = ({ items, itemHeight, containerHeight }) => {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef(null);
  
  // 보이는 아이템 개수 계산
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  
  // 버퍼 추가 (위아래 추가 렌더링)
  const buffer = 3;
  
  return (
    
{/* 구현 내용 */}
); };

Step 2: 가시 범위 인덱스 계산

스크롤 위치를 기반으로 렌더링할 아이템의 시작과 끝 인덱스를 계산합니다.

// 시작 인덱스 계산 (버퍼 포함)
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);

// 끝 인덱스 계산 (버퍼 포함)
const endIndex = Math.min(
  items.length - 1,
  startIndex + visibleCount + buffer * 2
);

// 실제 렌더링할 아이템들
const visibleItems = items.slice(startIndex, endIndex + 1);

Step 3: 스크롤 이벤트 핸들러 구현

const handleScroll = (e) => {
  const { scrollTop } = e.currentTarget;
  setScrollTop(scrollTop);
};

useEffect(() => {
  const container = containerRef.current;
  if (container) {
    container.addEventListener('scroll', handleScroll);
    return () => container.removeEventListener('scroll', handleScroll);
  }
}, []);

Step 4: 완전한 렌더링 로직

const VirtualScroll = ({ items, itemHeight = 50, containerHeight = 600 }) => {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef(null);
  
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  const buffer = 3;
  
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
  const endIndex = Math.min(
    items.length - 1,
    startIndex + visibleCount + buffer * 2
  );
  
  const visibleItems = items.slice(startIndex, endIndex + 1);
  
  // 전체 높이 계산
  const totalHeight = items.length * itemHeight;
  
  // 오프셋 계산
  const offsetY = startIndex * itemHeight;
  
  const handleScroll = (e) => {
    setScrollTop(e.currentTarget.scrollTop);
  };
  
  return (
    
{/* 스크롤바 높이 유지용 */}
{/* 실제 렌더링되는 아이템들 */}
{visibleItems.map((item, index) => (
{item}
))}
); };

5. 최적화 방법

1. 스로틀링(Throttling) 적용

스크롤 이벤트는 초당 수십 번 발생할 수 있습니다. 불필요한 리렌더링을 방지하기 위해 스로틀링을 적용합니다.

import { useCallback } from 'react';

const useThrottle = (callback, delay) => {
  const lastRun = useRef(Date.now());
  
  return useCallback((...args) => {
    const now = Date.now();
    if (now - lastRun.current >= delay) {
      callback(...args);
      lastRun.current = now;
    }
  }, [callback, delay]);
};

const handleScroll = useThrottle((e) => {
  setScrollTop(e.currentTarget.scrollTop);
}, 16); // 60fps

2. 동적 높이 지원

아이템마다 높이가 다른 경우, 각 아이템의 높이를 측정하고 누적 높이 배열을 유지합니다.

const [itemHeights, setItemHeights] = useState([]);
const cumulativeHeights = itemHeights.reduce((acc, h) => 
  [...acc, (acc[acc.length - 1] || 0) + h], []);

3. React.memo와 useMemo 활용

const VirtualItem = React.memo(({ item, style }) => (
  
{item}
));

6. 실전 활용 예제

10,000개의 사용자 목록을 표시하는 실전 예제입니다.

function App() {
  // 대용량 데이터 생성
  const users = useMemo(() => 
    Array.from({ length: 10000 }, (_, i) => ({
      id: i,
      name: `User ${i + 1}`,
      email: `user${i + 1}@example.com`
    })),
    []
  );
  
  return (
    

가상 스크롤링 사용자 목록

(
{user.name}
{user.email}
)} />
); }

이 구현으로 10,000개 아이템도 부드럽게 스크롤되며, 초기 렌더링 시간이 수 초에서 밀리초로 단축됩니다. React에서 가상 스크롤링 구현하기를 마스터하면 대용량 데이터 처리가 필요한 모든 프로젝트에서 성능을 극대화할 수 있습니다.

📚 함께 읽으면 좋은 글

1

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 22.
🎯 React에서 가상 스크롤링 구현하기

2

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 10.
🎯 React에서 가상 스크롤링 구현하기

3

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 8.
🎯 React에서 가상 스크롤링 구현하기

4

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 8.
🎯 React에서 가상 스크롤링 구현하기

5

React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 2.
🎯 React에서 가상 스크롤링 구현하기

💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!

📢 이 글이 도움되셨나요? 공유해주세요!

여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨

🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏

💬 여러분의 소중한 의견을 들려주세요!

여러분은 React에서 가상 스크롤링 구현하기에 대해 어떻게 생각하시나요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨

🔔 블로그 구독하고 최신 글을 받아보세요!

📚
다양한 주제
17개 카테고리

정기 업데이트
하루 3회 발행

🎯
실용적 정보
바로 적용 가능

💡
최신 트렌드
2025년 기준

🌟 알고리즘 해결부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨

📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!

답글 남기기