| 라이브러리 | 장점 | 단점 | 추천 사용처 |
|---|---|---|---|
| react-virtualized | 테이블, 그리드 지원, CellMeasurer 활용 가능 | 무겁고 복잡함 | 테이블, 가변 높이 리스트 |
| react-window | 성능 빠름, 가벼움, 간단한 API | 가변 높이 지원 X | 고정 크기 리스트, 무한 스크롤 |
| TanStack Virtual | 최신 기술 기반, 프레임 드랍 없음, 초경량 | 직접 구현 필요 | 커스텀 가상화, 초경량 가상화 |
react-virtualized 동작 원리List는 rowHeight와 rowCount를 기준으로 렌더링할 행의 위치를 계산합니다.rowHeight, scrollTop, height 값을 활용하여 렌더링할 행의 범위를 결정합니다.AutoSizerwidth와 height를 감지하고, 이를 List에 전달합니다.rowHeight의 역할List는 rowHeight 값을 기반으로 각 행의 위치를 계산합니다.rowHeight가 고정값이면 모든 행의 높이가 같다고 가정합니다.CellMeasurer를 사용해 각 행의 높이를 측정하고 캐싱합니다.🔥CellMeasurerCache:
defaultHeight로 기본 높이를 설정하되, 각 행의 높이를 실시간으로 측정합니다.🔥rowHeight 함수:
cache.rowHeight를 사용해 측정된 행 높이를 반환합니다.🔥CellMeasurer:
rowRenderer 안에서 각 행을 감싸, 높이를 동적으로 측정합니다.
// CellMeasurer를 위한 캐시 생성
const cache = new CellMeasurerCache({
fixedWidth: true, // 너비는 고정
defaultHeight: 100, // 초기 기본값
minHeight: 30, // 최소값
});
<AutoSizer>
{({width, height}) => (
<List
className="custom-list"
ref={listRef}
width={width}
height={height}
rowCount={comparsionData.length}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
/>
)}
</AutoSizer>
<List> 컴포넌트 옵션 정리react-window의 <List>는 세로 스크롤되는 1차원 리스트(Vertical List) 를 가상화하여 성능을 최적화합니다.
| 옵션명 | 타입 | 설명 | 기본값 |
|---|---|---|---|
height | number | 리스트 전체의 높이 (스크롤 가능한 영역) | 필수 |
width | number or "100%" | 리스트 전체의 너비 | 필수 |
itemCount | number | 리스트 아이템 개수 | 필수 |
itemSize | number | 각 행(row)의 높이 (픽셀 단위, 고정 크기) | 필수 |
overscanCount | number | 미리 렌더링할 추가 아이템 개수 (스크롤 성능 개선) | 1 |
className | string | List에 추가할 CSS 클래스명 | 없음 |
style | CSSProperties | List의 스타일 커스텀 | 없음 |
Grid는 2차원 가상화 (행 + 열)Grid는 "행(row)과 열(column) 둘 다 가상화" 할 수 있는 2차원 리스트입니다.
✔️ 스크롤 방향: 위 ↕️ 아래 + 좌 ↔️ 우
✔️ 가상화 적용 대상: 행(row) & 열(column)
✔️ 테이블, 데이터 그리드, Excel 같은 구조에서 필수!
✔️ 2차원 데이터를 표현할 때 적합 (예: 테이블, 대시보드, 캘린더)
📌 예제 (Grid)
<Grid
columnCount={10} // 10개의 열
rowCount={1000} // 1000개의 행 (모두 렌더링 X)
columnWidth={100} // 각 열의 너비
rowHeight={50} // 각 행의 높이
height={500} // 전체 그리드 높이
width={800} // 전체 그리드 너비
>
{({ columnIndex, rowIndex, style }) => (
<div style={{ ...style, border: "1px solid black" }}>
Cell {rowIndex}, {columnIndex}
</div>
)}
</Grid>
📝 결과:
Grid는 세로(height)와 가로(width) 스크롤이 모두 존재
- 그리드가 전체적으로 꽉 차지않고, width가 아주 일부분으로만 잡혀있는 현상
List or Grid의 부모 Container에 아래의 css를 입력
position: absolute
- table 구조를 가지고 <List 컴포넌트를 구현 해보려함
header의 width를 가지고, 동적으로 body의 각각의 Grid에 width를 적용 하려했으나.
위와같이 스크롤이 생기면서 th, td의 width가 맞지 않는 현상이 생김.
브라우저들의 스크롤 차이으로 인해 width가 다르므로
각 스크롤 부분 만큼의 width를 계산하여. 그만큼 동적으로 padding-right를 추가.
1.offsetWidth
2.clientWidth
3.scrollWidth
useEffect(() => {
setScrollbarWidth(getScrollbarWidth());
//`table` 크기 변경 감지 (리사이즈 대응)
const handleResize = () => {
setScrollbarWidth(getScrollbarWidth());
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
const getScrollbarWidth = () => {
// 스크롤바가 `clientWidth`에서 제외된 경우
if (tableRef.current.offsetWidth !== tableRef.current.clientWidth) {
return tableRef.current.offsetWidth - tableRef.current.clientWidth;
}
// MacOS처럼 `scrollbar`가 숨겨진 경우, `scrollWidth`로 비교
return tableRef.current.scrollWidth > tableRef.current.clientWidth
? tableRef.current.scrollWidth - tableRef.current.clientWidth
: 0;
}
- css border-collapse: collapse; 이슈
해당 css는 padding을 없애버리는 이슈가 있다.
그걸모르고 계속 위의 css가 걸려있는데도, 계속 padding을 주려고하였다.
css를 수정할수는 없어서. 아래와 같이 적용시켰다.
<BorderCollapseComponent>
<div style={{padding-right: ${scrollbarWidth}}}>
</div>
</BorderCollapseComponent>