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

개발 에러 해결 가이드 - FixLog 노트

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

대용량 데이터를 효율적으로 렌더링하는 것은 현대 웹 애플리케이션의 핵심 과제입니다. React에서 가상 스크롤링 구현하기는 수천, 수만 개의 아이템을 부드럽게 표시하는 최적화 기법으로, 화면에 보이는 영역의 요소만 렌더링하여 성능을 극대화합니다. 이 가이드에서는 가상 스크롤링의 핵심 원리부터 실전 구현까지 단계별로 살펴보겠습니다.

1. 가상 스크롤링 알고리즘 소개 및 개념

가상 스크롤링(Virtual Scrolling)은 뷰포트에 보이는 아이템만 DOM에 렌더링하고, 스크롤 시 동적으로 아이템을 교체하는 최적화 기법입니다. 일반적인 리스트 렌더링에서는 10,000개의 아이템이 있으면 모두 DOM에 생성되지만, 가상 스크롤링은 화면에 보이는 10~20개만 렌더링합니다.

핵심 개념은 다음과 같습니다:

  • 뷰포트(Viewport): 사용자에게 실제로 보이는 영역
  • 가상 높이: 전체 리스트의 높이를 계산하여 스크롤바를 올바르게 표시
  • 오프셋 계산: 스크롤 위치에 따라 렌더링할 아이템의 시작/종료 인덱스 결정
  • 버퍼 영역: 스크롤 시 깜빡임 방지를 위한 추가 렌더링 영역

2. 동작 원리 상세 설명

가상 스크롤링의 동작 과정은 다음 단계로 구성됩니다:

2.1 초기 계산 단계

컨테이너 높이와 아이템 높이를 기반으로 화면에 표시 가능한 아이템 개수를 계산합니다. 예를 들어 컨테이너 높이가 500px이고 각 아이템이 50px이라면 10개의 아이템이 보입니다.

2.2 스크롤 이벤트 처리

사용자가 스크롤하면 현재 스크롤 위치(scrollTop)를 기준으로 표시할 아이템의 시작 인덱스를 계산합니다. startIndex = Math.floor(scrollTop / itemHeight) 공식을 사용하여 첫 번째 아이템을 결정합니다.

2.3 렌더링 최적화

실제로는 보이는 영역보다 위아래로 버퍼 아이템을 추가로 렌더링합니다. 이는 빠른 스크롤 시 빈 공간이 보이는 현상을 방지합니다. 일반적으로 뷰포트의 50~100% 크기의 버퍼를 사용합니다.

2.4 위치 조정

렌더링된 아이템들을 올바른 위치에 표시하기 위해 transform: translateY()를 사용합니다. 전체 컨테이너는 가상 높이를 가지며, 실제 아이템들은 계산된 오프셋만큼 이동됩니다.

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

시간 복잡도

  • 초기 렌더링: O(k) – k는 뷰포트에 보이는 아이템 수 (상수)
  • 스크롤 처리: O(1) – 인덱스 계산은 산술 연산으로 즉시 수행
  • 아이템 업데이트: O(k) – 보이는 아이템만 재렌더링

일반 렌더링의 O(n)과 비교하면, n이 10,000개일 때 가상 스크롤링은 약 20개만 처리하므로 500배의 성능 향상을 얻을 수 있습니다.

공간 복잡도

  • DOM 노드: O(k) – 뷰포트 아이템 수에 비례
  • 메모리 사용: O(n) – 원본 데이터는 여전히 메모리에 유지
  • 상태 관리: O(1) – 스크롤 위치, 인덱스 등 고정된 상태값

일반 렌더링은 DOM 노드가 O(n)이지만, 가상 스크롤링은 O(k)로 메모리 사용량을 대폭 감소시킵니다.

4. 단계별 구현 과정

Step 1: 기본 구조 설정

먼저 필요한 상태와 ref를 정의합니다:

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

function VirtualScroll({ items, itemHeight = 50, containerHeight = 500 }) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef(null);
  
  // 화면에 보이는 아이템 수 계산
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  
  // 버퍼 아이템 수 (위아래 각각)
  const bufferCount = 5;
  
  return (
    
setScrollTop(e.target.scrollTop)} > {/* 내용은 다음 단계에서 추가 */}
); }

Step 2: 렌더링 범위 계산

스크롤 위치에 따라 표시할 아이템의 인덱스를 계산합니다:

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

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

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

Step 3: 가상 높이 및 오프셋 적용

전체 높이와 아이템 위치를 설정합니다:

// 전체 리스트의 가상 높이
const totalHeight = items.length * itemHeight;

// 렌더링되는 아이템들의 시작 위치
const offsetY = startIndex * itemHeight;

return (
  
setScrollTop(e.target.scrollTop)} > {/* 전체 높이를 유지하는 컨테이너 */}
{/* 실제 렌더링되는 아이템들 */}
{visibleItems.map((item, index) => (
{item.content}
))}
);

Step 4: 성능 최적화 – 스크롤 이벤트 쓰로틀링

빈번한 리렌더링을 방지하기 위해 쓰로틀링을 적용합니다:

import { useCallback, useRef } from 'react';

function useThrottledScroll(callback, delay = 16) {
  const lastRun = useRef(Date.now());
  
  return useCallback((e) => {
    const now = Date.now();
    
    if (now - lastRun.current >= delay) {
      callback(e);
      lastRun.current = now;
    }
  }, [callback, delay]);
}

// 사용 예시
const handleScroll = useThrottledScroll((e) => {
  setScrollTop(e.target.scrollTop);
}, 16); // 60fps

5. 최적화 방법

5.1 동적 아이템 높이 처리

고정 높이가 아닌 경우, 각 아이템의 높이를 측정하고 누적 합을 관리합니다:

const [itemHeights, setItemHeights] = useState({});
const itemRefs = useRef({});

useEffect(() => {
  const newHeights = {};
  Object.keys(itemRefs.current).forEach(key => {
    if (itemRefs.current[key]) {
      newHeights[key] = itemRefs.current[key].offsetHeight;
    }
  });
  setItemHeights(newHeights);
}, [visibleItems]);

5.2 React.memo로 불필요한 리렌더링 방지

React에서 가상 스크롤링 구현하기의 핵심은 효율적인 메모이제이션입니다:

const VirtualItem = React.memo(({ item, height }) => (
  
{item.content}
), (prevProps, nextProps) => prevProps.item.id === nextProps.item.id );

5.3 IntersectionObserver 활용

스크롤 이벤트 대신 IntersectionObserver를 사용하여 성능을 더욱 개선할 수 있습니다:

useEffect(() => {
  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // 아이템이 보일 때 처리
        }
      });
    },
    { root: containerRef.current, threshold: 0.1 }
  );
  
  return () => observer.disconnect();
}, []);

6. 실전 활용 예제

완성된 가상 스크롤링 컴포넌트를 실제 프로젝트에 적용하는 예제입니다:

// App.jsx
import VirtualScroll from './VirtualScroll';

function App() {
  // 10,000개의 대용량 데이터 생성
  const items = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    content: `아이템 ${i + 1}`,
    description: `설명 텍스트 ${i + 1}`
  }));
  
  return (
    

가상 스크롤링 데모

); }

이렇게 React에서 가상 스크롤링 구현하기를 완성했습니다. 대용량 데이터를 다루는 테이블, 무한 스크롤 피드, 채팅 애플리케이션 등에서 이 기법을 활용하면 사용자 경험을 크게 개선할 수 있습니다.

실무 팁

  • react-window, react-virtualized 같은 검증된 라이브러리 사용 고려
  • 모바일 환경에서는 터치 스크롤 성능에 특히 주의
  • SEO가 중요한 경우 서버 사이드 렌더링과의 호환성 검토
  • 접근성을 위해 키보드 네비게이션 지원 추가

가상 스크롤링은 현대 웹 애플리케이션의 필수 최적화 기법입니다. 이 가이드를 통해 개념부터 구현까지 완벽하게 마스터하셨기를 바랍니다!

📚 함께 읽으면 좋은 글

1

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

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

2

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

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

3

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

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

4

재귀 함수 마스터하기 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 8.
🎯 재귀 함수 마스터하기

5

배열 정렬 알고리즘 성능 비교 – 개념부터 구현까지 완벽 마스터

📂 알고리즘 해결
📅 2025. 10. 7.
🎯 배열 정렬 알고리즘 성능 비교

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

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

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


📘 페이스북


🐦 트위터


✈️ 텔레그램

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

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

React에서 가상 스크롤링 구현하기에 대한 여러분만의 경험이나 노하우가 있으시나요?

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

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

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

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

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

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

💡
최신 트렌드
2025년 기준

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

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

📱 전체 버전 보기