Home
PostsAbout
react

React의 윈도잉 기법

DOM 트리에 노드를 어디까지 늘릴 셈인가

2025-02-16

windowing 기법이란?

윈도잉 기법은 React에서 대규모 리스트를 효율적으로 렌더링하기 위해 사용되는 성능 최적화 기법입니다. 이 기법의 핵심 아이디어는 전체 리스트 중 현재 화면에 보이는 부분만 렌더링하는 것입니다.

image.png

윈도잉 기법은 슬라이딩 윈도우 기법이라는 이름과 함께 알고리즘 코딩테스트 문제에도 가끔 등장하는데요, 정해둔 길이의 윈도우를 들고 리스트의 특정 범위를 탐색할 때 쓰입니다.

페이스북 피드는 윈도잉 기법을 활용하는 대표적인 예시인데요. 페이스북은 피드를 무한 스크롤 방식으로 제공하며, 피드의 수가 매우 많기 때문에 성능 최적화가 필수적입니다. 이때 윈도잉 기법을 활용합니다.

잠깐 살펴보고 가는 페이스북 피드 성능 최적화 방법

  1. 초기 렌더링 최적화
    1. 사용자가 페이스북 앱이나 웹사이트에 접속했을 때 피드 전체를 한 번에 렌더링하지 않습니다.
    2. 첫 화면에 보이는 일부 피드만 렌더링하고, 사용자가 스크롤할 때 새로운 피드를 동적으로 로드합니다.
  2. 가상화 (Virtualization)
    1. 보이지 않는 피드는 DOM에서 제거하거나 메모리에 보관만 하고 렌더링을 최소화합니다.
    2. 이를 통해 브라우저가 관리하는 DOM의 크기를 줄이고 스크롤 성능을 향상시킵니다.
  3. 데이터 페칭 최적화 (Infinite Scroll)
    1. 스크롤 이벤트에 따라 새로운 데이터를 서버에서 가져와 동적으로 피드를 추가합니다.
    2. 페이스북은 GraphQL과 같은 기술을 활용하여 필요한 데이터만 효율적으로 페칭합니다.
  4. Lazy Loading (지연 로딩)
    1. 피드에 이미지나 동영상이 포함되어 있는 경우, 해당 미디어를 사용자가 스크롤로 해당 위치에 도달하기 전까지는 로드하지 않습니다.
    2. 이렇게 하면 네트워크 사용량이 줄어들고 초기 로딩 속도가 빨라집니다.
  5. 미디어 플레이어 최적화
    1. 사용자가 스크롤하여 동영상이 보이지 않게 되면 동영상 플레이어를 일시 정지하거나 메모리에서 언마운트합니다.
    2. 보이는 위치에 다시 돌아왔을 때만 동영상을 재생하여 자원을 효율적으로 관리합니다.

윈도잉 기법 및 리스트 가상화 기능을 제공하는 라이브러리

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2025-02-10_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_3.58.27.png

TanStack Virtual

TanStack Virtual은 최신 React 환경에 최적화된 가상화 라이브러리로, React 18 및 Concurrent Mode와 호환됩니다. 기본적으로 리스트 및 테이블 가상화를 제공하며, React Hook 기반의 설계로 인해 세밀한 컨트롤이 가능합니다.

또한, 스크롤 위치를 복원하는 기능을 지원하며, 서버사이드 렌더링(SSR)에도 친화적인 구조를 가지고 있습니다. 특히 React뿐만 아니라 Vue, Svelte 등 다양한 프레임워크와도 호환된다는 점이 장점입니다.

이 라이브러리는 최신 기술을 지원하면서도 상대적으로 가볍고 빠른 성능을 보이며, Sticky Headers 및 Infinite Scrolling을 쉽게 구현할 수 있습니다.

Headless UI이기 때문에 개발자가 디자인을 커스텀하면서 사용하기 용이하며, 제공하는 여러 기능들을 사용하기 편리하도록 훅을 제공합니다.

react-window

react-window는 리스트 및 그리드(테이블) 렌더링을 지원하는 가벼운 라이브러리로, 파일 크기가 4KB 미만이라는 점에서 매우 뛰어난 성능을 자랑합니다.

이 라이브러리는 FixedSizeList, VariableSizeList 등의 컴포넌트를 제공하여 다양한 크기의 아이템을 효율적으로 렌더링할 수 있도록 지원합니다.

단순한 API 구조 덕분에 학습이 쉬우며, 불필요한 DOM 요소를 제거하여 성능 최적화가 가능합니다. 그러나 무한 스크롤 기능을 기본적으로 제공하지 않으며, 가상화된 항목들의 높이가 변경될 경우 직접 관리해야 한다는 단점이 있습니다.

react-virtualized

react-virtualized는 react-window보다 다양한 기능을 제공하는 고급 라이브러리로, 리스트뿐만 아니라 Table, Grid, Masonry 같은 다양한 레이아웃을 지원합니다.

또한, 다중 열 정렬 및 가변 크기 행/열을 지원하며, 스크롤이 빠를 때 일정한 아이템 크기를 유지하는 CellMeasurer 기능을 제공합니다.

이 라이브러리는 InfiniteLoader 컴포넌트를 통해 무한 스크롤을 쉽게 구현할 수 있으며, CellMeasurer를 활용하면 아이템 크기를 동적으로 측정할 수도 있습니다.

하지만, react-window보다 파일 크기가 크고 성능이 상대적으로 낮으며, API가 복잡하여 학습 난이도가 높다는 단점이 있습니다. 게다가 현재 유지보수가 활발하지 않아 최신 React 환경과의 호환성이 점점 낮아지고 있는 점도 고려해야 합니다.

이전에 렌더링되었던 요소가 다시 화면에 나타날 때?

처음에는 렌더링됐지만 현재 사용자가 바라보고 있지 않은, 즉 이전에 봤지만 스크롤을 내렸기 때문에 화면 밖으로 사라진 요소들은 DOM에서 제거됩니다. 그렇다면 다시 스크롤을 올려서 해당 요소가 있던 자리로 돌아온다면 어떻게 동작할까요?

1. 상태(state) 기반으로 리스트를 다시 렌더링

윈도잉 기법을 적용한 라이브러리는 현재 보이는 리스트의 범위를 상태로 관리합니다. 사용자가 스크롤을 올리면 다음과 같은 순서로 요소가 다시 렌더링됩니다.

  1. 현재 스크롤 위치를 감지 → 새로운 요소가 뷰포트에 들어오는지 확인
  2. 새로운 요소가 보일 범위를 결정 → 기존 상태 업데이트
  3. 이전 요소를 다시 렌더링 → 필요하면 비동기 데이터 요청 수행
  4. DOM에 새로운 요소 추가 → 화면에 표시

이 과정에서 중요한 점은 이전 요소가 사라진 후 다시 생성될 때, 항목의 키(key) 값이 유지되면 동일한 데이터를 사용하여 복원될 수 있다는 점입니다.

예를 들어, react-window나 TanStack Virtual은 현재 보이는 아이템 인덱스(또는 ID)를 기반으로 렌더링 범위를 업데이트하기 때문에, 기존 요소가 사라졌다가 다시 나타나더라도 동일한 키 값을 가지면 새로운 DOM 요소가 만들어질 때 동일한 데이터를 다시 사용하게 됩니다.

2. 데이터 유지 vs 데이터 초기화

스크롤을 올려서 요소가 다시 나타났을 때, 해당 데이터가 유지되는 방식은 라이브러리 및 구현 방식에 따라 다릅니다.

  • 데이터 유지 방식:
    • react-window와 같은 라이브러리는 리스트의 데이터가 외부 상태(external state) 에 저장되어 있다면, 다시 스크롤을 올릴 때도 해당 데이터를 그대로 유지합니다.
    • 예를 들어, useState, Redux, React Query 같은 전역 상태 관리 도구를 사용하면 요소가 제거되었다가 다시 추가될 때 데이터가 초기화되지 않고 복원됩니다.
  • 이 방식은 특히 서버에서 데이터를 페칭하는 경우 유용합니다. 예를 들어, 스크롤 시 추가 데이터를 가져오는 무한 스크롤에서 기존 데이터를 캐싱하면 다시 불필요한 API 요청을 방지할 수 있습니다.
  • 데이터 초기화 방식:
    • 가상화 라이브러리에서 해당 아이템이 메모리에서 완전히 제거되었다면, 다시 나타날 때 새로운 데이터 요청이 발생할 수도 있습니다.
    • 예를 들어, react-virtualized에서 CellMeasurer를 사용하면 동적으로 높이가 변하는 요소가 다시 렌더링될 때 초기 크기로 다시 측정되는 현상이 발생할 수 있습니다.
    • 만약 가상화된 요소의 데이터를 유지하려면 캐싱 전략을 사용해야 합니다.

3. Scroll Position Restoration (스크롤 위치 복원)

윈도잉 기법을 사용할 때 가장 흔한 UX 문제 중 하나는 스크롤 위치 복원 문제입니다.

스크롤을 올려서 이전 요소로 돌아왔을 때 자연스럽게 보이게 하려면

  • 요소의 높이를 예측하여 미리 레이아웃을 설정하거나
  • 고정된 높이의 리스트를 사용하거나
  • TanStack Virtual의 estimateSize 옵션을 활용하면 됩니다.

예를 들어, TanStack Virtual에서는 다음과 같은 설정을 사용하여 이전 스크롤 위치를 복원할 수 있습니다.

import { useVirtualizer } from '@tanstack/react-virtual';

const rowVirtualizer = useVirtualizer({
  count: items.length,
  estimateSize: () => 50, // 아이템 높이를 미리 예측
  overscan: 5, // 추가적으로 미리 렌더링할 요소 개수
});

참고 자료