React

React 렌더링 최적화

이번 글에는 리액트 렌더링 최적화에 대해 공부한 내용을 적어두었다. 브라우저의 렌더링 과정부터 리액트의 Virtual DOM, React.memo, useCallback, useMemo의 활용까지 포괄적인 내용을 다루었다.

브라우저의 렌더링

notion image
 
브라우저의 렌더링 과정은 다음과 같다.
  1. HTML 파싱: DOM 트리 생성
  1. CSS 파싱: CSSOM 트리 생성
  1. 렌더 트리 생성: DOM과 CSSOM 결합
  1. 레이아웃(Reflow): 각 요소의 크기와 위치 계산
  1. 페인팅(Repaint): 화면에 픽셀 렌더링

React 렌더링

리액트는 DOM 수정을 최소화하여 성능을 최적화한다. 이는 Virtual DOM을 통해 구현된다.

Virtual DOM이란?

Virtual DOM은 메모리 내에 존재하는 가벼운 자바스크립트 객체로, 실제 DOM의 구조를 반영한 사본이다. 리액트는 UI 상태가 변경될 때마다 새로운 Virtual DOM을 생성하고, 이전 Virtual DOM과 비교하여 변경된 부분만 실제 DOM에 적용한다.

React의 렌더링 과정

리액트의 렌더링 과정은 크게 두 단계로 나눌 수 있다.
  1. 렌더 페이즈(Render Phase)
      • Virtual DOM을 생성 및 비교(디핑 Diffing)하는 과정이다. 이 단계에서는 실제 DOM에 변경이 가해지지 않는다.
      • 함수형 컴포넌트는 JSX를 리턴하며, React는 이를 React.Element로 변환하여 Virtual DOM을 생성한다.
      • 재조정(Reconciliation): 이전 Virtual DOM과 새로운 Virtual DOM을 비교하여 변경된 부분을 찾아내는 과정이다.
  1. 커밋 페이즈(Commit Phase)
      • 렌더 페이즈에서 찾은 변경 사항을 실제 DOM에 반영한다.
      • 커밋 페이즈는 세 가지 단계로 나뉜다.
          1. Before Mutation Phase: DOM 업데이트 전 작업
          1. Mutation Phase: DOM 업데이트 수행
          1. Layout Phase: 업데이트 후 작업

React의 성능 최적화

리액트는 성능 최적화를 위해 다양한 기법을 사용한다.
  1. 배치 업데이트(Batched Updates)
      • 여러 상태 변경을 하나의 업데이트로 배치 처리하여 불필요한 렌더링을 방지한다.
  1. 메모이제이션(Memoization)
      • 컴포넌트의 렌더링 결과를 메모이제이션하여 동일한 입력에 대해 재사용한다.
      • 리액트의 React.memo, useMemo, useCallback 훅을 사용하여 렌더링을 최적화할 수 있다.

useCallback과 useMemo

  • useCallback: 함수의 참조값을 유지하여 불필요한 함수 재생성을 방지한다.
    • const handleClick = useCallback(() => { // 함수 로직 }, [dependencies]);
      useCallback은 함수의 참조를 메모이제이션하여, 의존성 배열이 변경되지 않는 한 동일한 함수 참조를 유지한다. 이는 컴포넌트가 리렌더링될 때마다 새로운 함수 객체가 생성되지 않도록 하여, 디핑 과정에서 함수 참조의 변화를 감지하지 않게 한다. 따라서, 함수가 props로 전달되는 경우, 불필요한 리렌더링을 방지할 수 있다.
  • useMemo: 값의 참조를 유지하여 불필요한 계산을 방지한다.
    • const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
      useMemo는 계산된 값을 메모이제이션하여, 의존성 배열이 변경되지 않는 한 동일한 값을 반환한다. 이는 컴포넌트가 리렌더링될 때마다 동일한 값이 사용되도록 하여, 디핑 과정에서 값의 변화를 감지하지 않게 한다. 따라서, 값이 props로 전달되는 경우, 불필요한 리렌더링을 방지할 수 있다.

React.memo

  • React.memo는 전달받은 props가 이전과 동일하면 컴포넌트를 리렌더링하지 않는다.
    • const MyComponent = React.memo(({ prop1, prop2 }) => { // 컴포넌트 로직 });
      React.memo는 이전 렌더링과 새로운 렌더링의 props를 비교하여 동일한 경우, 해당 컴포넌트의 리렌더링을 막아준다. 이는 렌더 페이즈와 커밋 페이즈 모두에서 적용되며, 특히 커밋 페이즈에서 불필요한 DOM 업데이트를 방지하여 성능을 최적화한다.

React Fiber와 최적화

React Fiber는 리액트의 새로운 재조정 알고리즘으로, 더 나은 성능과 유연성을 제공한다. Fiber는 작업을 더 작은 단위로 나누어, 긴 작업이 사용자 인터페이스를 차단하지 않도록 한다.

Fiber Node와 최적화

React Fiber는 각 컴포넌트를 Fiber Node로 표현한다. useMemouseCallback을 사용하면 메모이제이션된 값이나 함수는 해당 Fiber Node에 저장된다. 이는 컴포넌트가 리렌더링되더라도 메모이제이션된 데이터를 재사용할 수 있게 하여 성능을 최적화한다.
리액트 애플리케이션의 성능을 최적화하기 위해 React.memo, useCallback, useMemo와 같은 훅을 적절히 사용해야 한다. 또한 브라우저의 렌더링 과정과 리플로우, 리페인트의 개념을 이해하고, React Fiber의 특성을 활용하면, 더 효율적이고 반응성이 뛰어난 애플리케이션을 개발할 수 있다.