2024. 1. 11. 08:47ㆍ프론트엔드/React
React-virtual?
React-virtual 라이브러리는 Tanstack 이라는 단체에서 관리되는 DOM 가상화 라이브러리이다.
리액트의 옛 공식문서에서 확인할 수 있 듯, 거대한 크기의 리스트를 화면에 랜더링할 경우, "가상화" 라는 방법을 통해 최적화 하는것을 권한다.
사용 필요성
모든 SNS 에 필수적으로 들어가는 기능인 무한 스크롤 (Infinite scroll) 은 무한 스크롤은 웹 페이지에서 사용자가 스크롤을 내릴 때 자동으로 추가 콘텐츠를 로드하여 페이지를 끊임없이 확장하는 기술로, 스크롤을 끝까지 내리면 새로운 콘텐츠가 동적으로 로드되어 사용자 경험을 향상시킨다.
또한 페이지 이동 없이 사용자가 머무는 시간을 늘려 사용자 유지율을 향상시킬 수 있어 웹 어플리케이션에 자주 사용되는 기술 이다.
하지만 페이지네이션과 다르게 무한 스크롤 기법은 이름답게 데이터만 충분하다면 DOM에도 무한한 요소들이 생성될 수 있다. (물론 그 전에 브라우저가 뻗을것이다). 이는 곧 성능 저하를 유발하고, UX에 악영향을 미치게 된다.
Windowing (Virtualization) 기법
이럴때 사용할 수 있는 기법이 Virtualization (Windowing) 기법인데, 브라우저 뷰포트를 벗어난 부분의 요소는 랜더링하지 않는 방식으로 브라우저의 부담을 줄이는 기법이다.
마치 창문을 통해 바깥을 바라볼 때 창문이 열린 부분 만 보이는 것과 같이 보이는 부분만 렌더링을 하는 기법이다.
라이브러리 선택기준
다행히도 이러한 기법을 쉽게 사용할 수 있도록 해주는 라이브러리가 여러가지가 있는데, 대표적으로 아래와 같은 라이브러리가 있다.
TanStack Virtual | React Virtual, Solid Virtual, Svelte Virtual, Vue Virtual
TanStack Virtual v3 Headless UI for Virtualizing Large Element Lists Virtualize only the visible DOM nodes within massive scrollable elements at 60FPS in TS/JS, React, Vue, Solid & Svelte while retaining 100% control over markup and styles.
tanstack.com
react-virtualized
React components for efficiently rendering large, scrollable lists and tabular data. Latest version: 9.22.5, last published: 9 months ago. Start using react-virtualized in your project by running `npm i react-virtualized`. There are 1638 other projects in
www.npmjs.com
react-window
React components for efficiently rendering large, scrollable lists and tabular data. Latest version: 1.8.10, last published: 2 months ago. Start using react-window in your project by running `npm i react-window`. There are 1620 other projects in the npm re
www.npmjs.com
라이브러리를 선택하는 기준은 각각의 개발자 마다 다르나, 살펴봐야 할 것 들이 몇 가지있다.
1. 유지보수가 잘 이루어지고 있는지
2. 모듈 사이즈
3. 다운로드수
4. 공식문서, 예제
대표적으로 위와 같은것들이 있는데, Npm Trends 를 한번 살펴보면 아래와 같다
순위 | 모듈 사이즈 | 유지보수 | 다운로드수 | 공식문서 |
1 | @tanstack/react-virtual | @tanstack/react-virtual | react-virtualized | @tanstack/react-virtual |
2 | react-window | react-virtualized | react-window | react-virtualized |
3 | react-virtualized | react-window | @tanstack/react-virtual | react-window |
다운로드 수는 1,152,282로 react-virtualized 가 가장 많은 것을 볼 수 있고,
업데이트 주기는 @tanstack/react-virtual 이 가장 최근 (1달 전) 에 이루어 졌다.
번들의 사이즈는 @tanstack/react-virtual가 gzip 기준 4.0kb 로 가장 작고,
공식문서의 경우 주관적일 수 있는데 개인적으로 @tanstack/react-virtual 이 가장 잘 정리되어 있었다고 생각한다.
무엇보다 @tanstack/react-query 를 유지보수하는 tanstack 에서 유지보수 하고 있기에 무한스크롤에 적용하는 예시 역시 잘 나와있었다.
진행 하는 프로젝트에서도 react-query 를 이용하고 있었기에 최종적으로 @tanstack/react-virtual를 사용하기로 결정을 했다.
코드
Windowing 적용전 List 컴포넌트
import generateArray from "./generateArray.ts";
const LargeList = () => {
const dummyArr = generateArray();
return (
<div className="stack">
<p>Windowing 미적용</p>
<div className="stack">
{dummyArr.map(({ content }) => (
<div className="item">{content}</div>
))}
</div>
</div>
);
};
export default LargeList;
Windowing 적용된 List 컴포넌트
import generateArray from "./generateArray.ts";
import { useRef } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
const VirtualLargeList = () => {
const dummyArr = generateArray();
const parentRef = useRef(null);
const rowVirtualizer = useVirtualizer({
count: dummyArr.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 36,
overscan: 3,
});
return (
<div className="stack">
<p>Windowing 적용</p>
<div className="stack" ref={parentRef}>
<div
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
width: "100%",
position: "relative",
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div
key={virtualRow.index}
className={"item"}
style={{
position: "absolute",
width: "100%",
height: "36px",
transform: `translateY(${virtualRow.start}px)`,
}}
>
{dummyArr[virtualRow.index].content}
</div>
))}
</div>
</div>
</div>
);
};
export default VirtualLargeList;
간단하게 라이브러리의 작동원리를 살펴보면, useVirtualizer 라는 Hook은 부모컴포넌트의 Ref와
자식요소의 대략적인 크기를 입력 받아서 해당 ref 를 통해 총 요소의 hegiht , scroll 위치 등의 정보들을 계산하고,
이를 바탕으로 해당 컴포넌트의 시작위치, 보여져야하는 컴포넌트 인덱스 등이 담긴 Row 를 return 한다. 이를 바탕으로 가상화 자식 요소들의 엘리먼트를 translate 로 이동시키고 나머지 요소들은 DOM 에서 제거하는 형태로 scroll이 가능한 가상 리스트를 구현할 수 있다.
적용 결과
Windowing 적용 전
Windowing 적용 후
성능측정
실제 windowing 을 적용한 프로젝트에는 아직 데이터가 많이 없고 여러가지 변수가 많기에 Windowing 적용 전후 결과를 벤치마크 하기엔 좋은 본보기가 되지 못한다. 따라서 Code Sandbox 에서 따로 테스트용 프로젝트를 만들어서 진행했다.
테스트환경
성능 측정은 크롬브라우저의 성능탭을 사용했고
CPU 쓰로틀링 X6 을 걸었고
총 5000개의 리스트를 렌더링 하는 방식에서
브라우저 새로고침 후 페인트에 걸린 시간을 측정했다.
페인팅 시간 1 / 10 로 단축
가상화 적용 전에 페인팅에 걸린 시간은 322밀리초로 측정되었고, 가상화 적용 후, 페인팅에 걸린 시간은 31밀리초로 약 1/10 수준으로 줄어들었다.
LCP (Largest Contentful Paint) 850ms 단축
LCP 의 경우 네트워크 지연 등의 변수가 있어서 정확한 지표가 될 수는 없지만
결과적으로 가상화 적용 전에 LCP 는 8.42초, 가상화 적용 후, LCP는 7.57 초로, 850ms 줄었다.
결론.
무한스크롤가 포함된 프로젝트 뿐만 아니라 대시보드와 같은 곳에서도 대량의 리스트를 랜더링 해야하는 경우는 종종 있기에 가상화 / windowing 은 프론트엔드 개발을 할 때 알아두면 좋은 최적화 기법이라고 할 수 있다.
'프론트엔드 > React' 카테고리의 다른 글
[React] useState 를 이용한 상태관리 (0) | 2022.08.03 |
---|---|
[React] 리액트 컴포넌트의 속성 Props (0) | 2022.08.02 |
[React]리액트 공부 시작! - 리액트의 특징 (0) | 2022.08.02 |