React같은 SPA 라이브러리에서는 효율적인 렌더링을 하기 위해 실제 DOM오브젝트의 복사본 혹은 추상화를 하여 diffing 알고리즘이나 reconciliation 프로세스를 통해 실제 DOM과 동기화해서 실제 DOM에서 변경된 부분만 감지해서 렌더링을 수행하기 위해서 리액트가 생성하는 가상 DOM 오브젝트를 Virtual DOM 이라고 한다.
reconciliation 이 어떤식으로 동작하는지는 별도의 글을 써서 알아보기로 하고 지금은 Virtual DOM 에 집중해보자
1. 왜 React는 Virtual DOM을 쓰지? 🤔
왜 리액트는 Virtual DOM 을 쓰는지 이해하기 위해선 브라우저가 실제 그림을 그리는 렌더링 과정을 이해할 필요가 있다.
1. DOM 생성 : 브라우저는 HTML을 받아서 DOM(Document Object Model) 트리를 만든다 이 트리는 웹 페이지의 모든 요소와 속성, 그리고 관계를 나타낸다
2. 렌더 트리 생성 : CSSOM (CSS Object Model) 과 DOM을 합쳐서 렌더트리를 만든다. 여기서 보이지 않는 요소는 제외된다. ex) display: none
3. 레이아웃 : 렌더링 엔진은 각 노드가 화면의 어떤 위치에 어떻게 배치되어야 하는지 계산한다.
4. 페인팅 : 각 요소의 색상, 이미지등 실제 내용을 화면에 그린다.
렌더링은 위 과정을 거치는데 인터랙션에 의해서 요소가 바뀔때 마다 모든 DOM을 처음부터 다시 그려야 한다면 사용자가 경험측면에서도 좋지 않고 앱이 커질수록 속도가 느려지게 될 수 있다.
예를 들어서 어떤 게시글의 좋아요를 누른다거나 장바구니의 수량조절 버튼을 눌러서 숫자를 변경한다던지 그러한 동작들이 발생할때마다 모든 DOM의 렌더링과정을 다시 계산한다면 비효율적일 것이다.
그래서 React는 Virtual DOM을 사용해 전체 DOM을 다시 그리는게 아니라 실제 바뀐 부분만 따로 인식해서 그 DOM만 다시 그리도록 업데이트 한다
💡 브라우저 별 렌더링 엔진
- Chrome, Opera, Edge 에서는 Blink 라는 구글의 오픈소스 렌더링 엔진을 사용한다. - Firefox는 Gecko라는 자체 개발 엔진을 사용한다. - Safari는 Apple이 개발한 Webkit엔진을 사용한다.
😎 https://csstriggers.com/ 라는 페이지에 들어가보면 각 렌더링 엔진 별로 어떤 css 옵션이 위 렌더링 과정중 어떤 과정이 일어나고 안일어나는지 정리해놓았다
위 코드처럼 상태변화에 따라 다른 컴포넌트를 그려줄때 Virtual DOM을 사용한다고 할때
먼저 모든 React DOM 엘리먼트는 대응하는 Virtual DOM object 를 가지고 있고 그 Virtual DOM이 DOM 하나하나에 매핑되고 데이터가 변경되었을때 바뀐 데이터를 바탕으로 React.createElement 로 새로운 JSX 엘리먼트를 생성해 렌더링한다! 그리고 이때 모든 Virtual DOM object를 업데이트 한다
위에서 언급했다시피 Virtual DOM을 업데이트 하는것은 비용이 많이 들지 않는다 그래서 이 업데이트 된 Virtual DOM을diffing 알고리즘을 통해 이전의 Virtual DOM 스냅샷이랑 비교해서 정확히 어떤 요소가 바뀌었는지 검사하고 변경이나 업데이트가 마무리 되면 실제 DOM 에 업데이트를 해준다
🌞 diffing 알고리즘에 대한 간단한 설명
1. 엘리먼트의 속성 값만 변한 경우 새로운 Virtual DOM을 생성하지 않고 속성 값만 업데이트 해준다 2. 엘리먼트의 태그 또는 컴포넌트가 변경된 경우 해당 노드를 포함한 자식노드를 제거한 뒤 새로운 Virtual DOM을 생성해 대체 해준다!
🚨 무조건 Virtual DOM이 좋은가?
성능이 좋더라도 Virtual DOM 을 생성하고 비교하는 추가작업이 들어가기 때문에 인터랙션이 없는 단순 정보제공 페이지라면 DOM트리의 변화가 발생하지 않아 일반 DOM의 성능이 더 좋을 수 있다!