기술 블로그
React 대량 데이터 편집 모달에서 입력 지연 원인 추적기 본문

[React] 대규모 데이터 편집 시 발생하는 입력 지연 분석: Fiber 트리 순회와 브라우저 Reflow
React 환경에서 수천 건의 데이터를 동시에 다루는 편집 UI를 구현하다 보면, 단순한 최적화만으로는 해결되지 않는 성능 병목을 마주하곤 합니다. 최근 약 3,800건의 매출 데이터를 수정하는 모달에서 입력 시 약 5초간의 지연(Input Lag)이 발생했던 사례를 통해, React 내부 동작과 브라우저 렌더링 엔진의 관점에서 그 원인을 분석하고 해결 과정을 기록합니다.
1. 현상 및 초기 진단
- 규모: 약 3,822행 (행당 Input 및 SelectBox 등 10여 개의 컴포넌트 포함)
- 문제: 특정 입력 필드에 타이핑 시 화면이 수 초간 프리징됨.
- 초기 가설: 검색 필터링 로직의 부하 혹은 부모 컴포넌트의 불필요한 리렌더링을 의심함.
그러나 성능 측정 결과, 필터링 로직은 1ms 미만으로 수행되었으며, onBlur 처리를 통해 부모의 리렌더링을 억제했음에도 지연 현상은 개선되지 않았습니다.
2. React.memo와 Fiber 트리 순회 비용
Chrome DevTools의 Performance 프로파일링을 통해 확인한 결과, JavaScript 실행 시간(Yellow Block)이 병목의 큰 비중을 차지하고 있었습니다. 특히 React 내부의 recursivelyTraverseMutationEffects 함수가 수만 번 호출되는 것을 확인했습니다.
분석 결과 비교
| 최적화 단계 | JS 실행 시간 (Performance) | 특이 사항 |
|---|---|---|
| 기존 (No Memo) | 4,366ms | 모든 Fiber 노드를 전수 조사 |
| React.memo 적용 후 | 2,983ms | 변경되지 않은 서브트리 스킵 |
React는 상태 변경 시 Fiber 트리를 순회하며 업데이트가 필요한 노드를 탐색합니다. 행당 컴포넌트가 복잡할수록(Hook 사용량이 많을수록) Fiber 노드의 수는 기하급수적으로 증가하며, React.memo가 없을 경우 단 하나의 입력 변경에도 수만 개의 노드를 재귀적으로 방문하게 됩니다.
React.memo 적용 시, 변경되지 않은 행의 subtreeFlags를 확인하여 해당 서브트리 전체를 건너뛰기 때문에 JS 실행 비용을 유의미하게 절감할 수 있었습니다.
3. 두 번째 병목: 브라우저 렌더링 엔진의 한계 (Reflow)
JavaScript 실행 시간을 단축했음에도 불구하고, 프로파일러상에는 여전히 보라색 블록(Layout/Reflow)이 선명하게 남아 있었습니다. 이는 React의 영역을 넘어 브라우저 렌더링 엔진이 처리해야 할 물리적인 한계치에 도달했음을 의미합니다.
- DOM 노드 규모: 3,822행 × 11개 셀 = 약 10만 개의 DOM 노드
- 원인:
input의 value가 변경될 때마다 브라우저는 DOM에 존재하는 10만 개의 노드를 대상으로 레이아웃을 재계산합니다. - 한계:
React.memo는 JavaScript 엔진의 부하를 줄여줄 뿐, 이미 DOM에 그려진 수많은 노드로 인한 브라우저의 Reflow 비용은 해결할 수 없습니다.
4. 종합 및 최종 해결책
이번 디버깅을 통해 파악한 성능 병목의 구조는 다음과 같습니다.
| 병목 레이어 | 원인 | 대응 방안 | 비고 |
|---|---|---|---|
| JavaScript (React) | Fiber 트리 재귀 순회 부하 | React.memo | 서브트리 스킵 가능 |
| Browser (Rendering) | 10만 개 DOM 노드의 Reflow | Virtualization | DOM 노드 수 자체를 제한 |
결론적으로 가상화(Virtualization)는 단순히 리렌더링 성능만을 위한 도구가 아닙니다.
- Fiber 트리의 규모를 물리적으로 축소하여 React의 순회 비용을 최소화하고,
- DOM 노드 수를 수백 개 수준으로 유지하여 브라우저의 레이아웃 계산 부하를 근본적으로 제거하는 가장 확실한 전략입니다.
5. 마치며
대규모 데이터를 다루는 UI에서는 "느림"의 원인이 복합적으로 작용할 때가 많습니다. 이번 사례처럼 JS 실행 비용과 브라우저 렌더링 비용이 혼재된 경우, 단계별 측정을 통해 병목의 실체를 명확히 구분하는 과정이 필수적입니다.
'프론트엔드' 카테고리의 다른 글
| 5,000건 대량 테이블의 '전체 선택' 지연 해결하기 (Virtualization) (0) | 2026.03.19 |
|---|---|
| 체크박스 아이템 최적화 (0) | 2026.03.13 |
| 초성 검색을 지원하는 Local Async SelectBox 구현하기 (0) | 2026.02.09 |
| [rn] prebuild + fastlane 같이 사용하기 (1) | 2025.08.05 |
| expo prebuild로 프로덕션 빌드하기 (0) | 2025.07.25 |
