본문 바로가기

FrontEnd

리액트 성능 최적화 : Virtual DOM

반응형

1. Virtual DOM은 무엇인가요? (공식 문서)

Virtual DOM (VDOM)은 UI의 "이상" 또는 “가상”적인 표현을 메모리에 저장하고
ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다..
실제 DOM과 Virtual DOM을 동기화하는 과정을 재조정이라고 합니다.

Virtual DOM은 React의 선언적 API를 가능하게 합니다.
React에게 원하는 UI의 상태를 알려주면 DOM이 그 상태와 일치하도록 합니다.
이러한 방식은 앱 구축에 사용해야 하는 어트리뷰트 조작, 이벤트 처리, 수동 DOM 업데이트를 추상화합니다.

Data 조작을 통해 UI를 업데이트하여 돔 조작 코드를 제거하여 생산성을 향상시킵니다.

 

2. 리액트 컴포넌트의 생명 주기

React 앱의 라이프사이클은 다음과 같다.

→  render → reconciliation → commit
         ↖                   ↙
              state change
  • render phase : React.createElement로 리액트 엘리먼트를 생성
  • recolcilation phase : 이전 엘리먼트와 새로운 엘리먼트를 비교
  • commit phase : 변경된 부분만 dom에 반영

Mount : 컴포넌트가 처음 생성되어 DOM에 반영되는 과정
Render : virtual DOM 내부에서 새로 반영할 부분을 계산하는 단계

  • virtual DOM 내부 부분에 Bold를 준 이유가 있다. Devtools에서 깜빡임은 이 단계를 의미하는 것이다.
  • 해당 컴포넌트가 reconcilation단계 후 새로 그려질 부분이라고 인식하는 것이다. 해당 컴포넌트 대상으로 reconcilation을 수행한다.

Commit : virtual DOM 내부에는 fiber라는 자료구조가 있고, 파이버 내부에 반영할 실제 DOM Component가 있다. render phase에서 만들어진 해당 DOM을 실제 DOM에 반영한다.

즉, devtools의 깜빡임은 브라우저의 reflow와 repaint를 의미하는게 아니라 렌더링을 의미한다.
react는 reconcilation 결과를 실제 돔에 반영할 것이다. (내부적으로 document api를 사용하여)
그러면 reflow/repaint 단계가 수행될 것이기에 해당 불필요한 작업을 피하고자 react의 최적화 API를 사용하는 것이다.

1. 리액트는 virtual DOM을 사용한다.
2. 리액트는 render phase에서 실제 돔과 virtual 돔의 차이를 계산한다.
3. 2번에서 devtools가 highlight하는 부분은 rendering된 컴포넌트다.
4. react는 렌더링 후 reconcilation 결과를 실제 dom에 삽입(commit)한다.
5. 마운트와 페인트는 다르다

컴포넌트의 리렌더링에 따른 이펙트가 실행되는 순서

3. 리액트가 실제 DOM Tree에 해당 컴포넌트를 반영하는 방법.

비교하는 Element 가 다른경우 ex : div -> section

  1. 이전 Virtual DOM 을 제거한다. (componentWillUnmount, useEffect clean-up)
  2. 이때 모든 자식들도 모두 제거되고 재생성 된다.

비교하는 Element 와 Component 가 같은 경우 ex : div -> div

  1. Virtual DOM 의 property 를 수정한 뒤, 자식들의 노드를 비교한다. (componentWillUpdate, useEffect diff)
  2. 이때 자식들은 key 를 가질 수 있는데, 이전과 변경되는 key 를 비교한다.
  3. key 가 없다면, 이전 자식 node 를 삭제하고, key 가 추가되었다면, 새로 자식 node 를 생성한다.
  4. key 가 같다면, 비교를 진행한다.

4. 리액트 Virtual DOM의 ReRendering이 일어나는 상황

  1. 부모 Component 가 Render
  2. props 가 변경 되었을 때
  3. state 가 변경 되었을 때
  4. forceUpdate 가 호출되었을 때 (훅이랑은 별 상관 없음)
  5. contextProvier의 state가 변경되었을 때, consumer의 리렌더링.

메모를 이용하여 자식 컴포넌트의 리렌더링 막는 법

const Component = (props) => (<div>{JSON.stringify(props)}</div>)
// 1. 자식 컴포넌트에 React.Memo를 적용한다.
const Memo = React.memo(Component,
	// 깊은 비교를 수행하게 한다.
  (prevProps, nextProps) => {
     return JSON.stringify(prevProps) === JSON.stringify(nextProps);
  }
);

// 부모 컴포넌트가 리렌더링되면 자식 컴포넌트가 반드시 호출된다. 이는 리렌더링으로 이어진다.
// React.memo를 활용하면 불필요한 렌더링을 줄일 수 있으나, 비교 연산이 비쌀 수 있음을 유의한다.
const Parent = (a,b)=>{
    // 2. 함수 컴포넌트가 호출되면 반드시 함수가 새로생긴다. 이는 자식 컴포넌트에서 깊은 복사를 수행하지 않으면
    // 반드시 리렌더링을 발생시킨다. 이를 막기 위해 useCallback을 활용할 수 있다.
    const myFunction = useCallback(()=>{},[])
    // 3. 2와 같은 이유로 해당 값을 계속 재사용한다면 이 값을 이용할 수 있다.
    const sum = useMemo(() => {
    return a + b;
    }, [a, b]);
    return(<Memo props={{a,b,myFunction,sum}}/>

}

React.Memo의 기본 비교 동작을 보고 싶으면 아래 코드를 참조하자.
react/shallowEqual.js at v16.8.6 · facebook/react (github.com)

 

facebook/react

A declarative, efficient, and flexible JavaScript library for building user interfaces. - facebook/react

github.com


참고 자료 : (React) React의 생명 주기(Life Cycle) - 라이프사이클 - ZeroCho Blog

 

(React) React의 생명 주기(Life Cycle) - 라이프사이클

안녕하세요. 이번 시간에는 React의 생명 주기에 대해서 알아보겠습니다. 갑자기 웬 생명 주기냐고요? React 컴포넌트는 생명 주기가 있습니다. 생애 주기 또는 라이프사이클(Life cycle)이라고도 많

www.zerocho.com

React Training: React: "mount" vs "render"?

 

ReactTraining.com

React Corporate Workshops, Training, and Consulting

reacttraining.com

React 의 성능을 조금 이라도 올려보자 (Performance Optimize) | by 박성룡 ( Andrew park ) | Medium

 

React 의 성능을 조금 이라도 올려보자 (Performance Optimize)

React 의 성능을 올리기 위해 최적화 방법을 정리 해보고자 한다.

pks2974.medium.com

Render Performance Optimization With React - Adaptive Financial Consulting (weareadaptive.com)
+ 리액트 공식문서

반응형