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가 중요한 경우 서버 사이드 렌더링과의 호환성 검토
- 접근성을 위해 키보드 네비게이션 지원 추가
가상 스크롤링은 현대 웹 애플리케이션의 필수 최적화 기법입니다. 이 가이드를 통해 개념부터 구현까지 완벽하게 마스터하셨기를 바랍니다!
📚 함께 읽으면 좋은 글
React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터
📅 2025. 10. 8.
🎯 React에서 가상 스크롤링 구현하기
React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터
📅 2025. 10. 8.
🎯 React에서 가상 스크롤링 구현하기
React에서 가상 스크롤링 구현하기 – 개념부터 구현까지 완벽 마스터
📅 2025. 10. 2.
🎯 React에서 가상 스크롤링 구현하기
재귀 함수 마스터하기 – 개념부터 구현까지 완벽 마스터
📅 2025. 10. 8.
🎯 재귀 함수 마스터하기
배열 정렬 알고리즘 성능 비교 – 개념부터 구현까지 완벽 마스터
📅 2025. 10. 7.
🎯 배열 정렬 알고리즘 성능 비교
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
React에서 가상 스크롤링 구현하기에 대한 여러분만의 경험이나 노하우가 있으시나요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 알고리즘 해결부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!