2020. 9. 13. 23:56ㆍFrontend
Overview
React는 프론트엔드 웹 애플리케이션을 개발자의 입장에서 조금더 편하고 직관적으로 개발할 수 있도록 도와주는 자바스크립트 라이브러리입니다. 처음 React로 개발을 시작하게 되면 아마도 가장 먼저 듣게 되는 정보가 바로 가상돔(Virtual DOM)에 관한 내용입니다. 그만큼 React에서 핵심적인 기능을 담당하고 있는 것이 바로 Virtual DOM인데, 이번 포스팅에서는 이 Virtual DOM에 대한 간단한 개념을 살펴보고 왜 Virtual DOM이 필요한지, 그리고 Virtual DOM은 어떻게 동작하는지를 간단하게 살펴보도록 하겠습니다.
Virtual DOM & DOM
DOM은 Document Object Model의 약자로 간단하게 말하면, 브라우저가 HTML문서를 해석하는 방식을 의미합니다. HTML파일은 말 그대로 태그들로 이루어진 텍스트인데, 이를 브라우저가 화면에 그려내기 위해서 아래와 같이 자기가 이해하기 쉬운 객체 트리 방식으로 변환하는 것입니다. 이를 DOM tree라고 합니다.
이렇게 브라우저가 HTML Parser를 통해 HTML 태그들을 다음과 같이 DOM tree를 만들고 나면 CSS를 적용하여 Render Tree를 생성하게 되고, 이 렌더 트리가 생성되고 나면 Reflow 와 Repaint과정등, 브라우저에서 각 노드들의 좌표를 계산하고 색깔을 칠하는 과정들을 거친 후에 최종적으로 화면에 그려내게 되는 것입니다.
여기까지는 기본적인 DOM인데, 그렇다면 Virtual DOM은 무엇이고, 이것이 왜 필요할까요?
Virtual DOM은 간단하게 말해서 DOM차원에서의 '더블 버퍼링 개념'을 의미합니다. 실제로 브라우저가 렌더링하고 그려내는 DOM이 아니라, 중간에 가상의 DOM을 두어 개발의 편의성과 성능을 개선하도록 하는 것입니다. 즉, 개발자가 DOM을 조작할 때, 자바스크립트로 직접 브라우저의 DOM을 조작하는 것이 아니라 가상의 DOM을 조작하게 되고, 리액트는 이 가상의 DOM 조작이 어느 정도 마무리되면 변경사항을 한꺼번에 실제 브라우저의 DOM에 반영하는 방식입니다.
왜 이런 방식을 사용하게 되었는지는 반응형 웹의 등장을 통해 설명할 수 있습니다. 예전에 정적인 텍스트 파일들과 간단한 css만 제공하면 되었었던 웹앱이 지금은 반응형으로 바뀌면서 비교할 수 없을 만큼 많은 DOM조작을 요구하게 되었습니다. 버튼에 마우스를 가까이 가져다 댔을 때 버튼의 배경색이 바뀐다던지, 팝업이 뜨면서 광고내용이 나온다던지 하는 내용들은 모두 브라우저가 DOM을 조작하는 행위입니다.
물론 브라우저는 API를 통해 DOM을 조작하는 방식들을 제공합니다. 하지만 문제는 DOM을 직접 조작하는 행위는 브라우저로 하여금 각 조작마다 reflow 와 repaint를 수반하게 한다는 점입니다. 예를 들어 DOM 조작을 30번 실행하는 코드가 있다고 했을 때, 만약 이 코드가 브라우저의 DOM을 직접 조작하도록 설계되었다면, 브라우저는 실제로 사용자가 보기에 그렇게 많은 내용이 바뀌지 않음에도 불구하고 30번이나 모든 노드들의 위치와 css를 다시 계산하고 그려내야 한다는 것입니다.
Virtual DOM은 바로 이러한 상황을 효율적으로 개선하기 위해서 고안된 개념입니다. Virtual DOM이 있다면, 개발자들은 DOM을 직접 조작하게 됩니다. Virtual DOM은 브라우저가 해석하는 대상이 아니기 때문에, 변경되어도 브라우저가 repaint, reflow등의 연산을 수행하지 않습니다. 따라서 위의 상황과 같이 DOM을 30번 조작하는 경우가 생긴다면 virtual DOM은 이를 Batch Queue에 모아둔 후에, 이를 모두 처리하고 나서 한꺼번에 업데이트 사항을 브라우저의 DOM에 반영하게 됩니다. 결과적으로는 1번만 DOM을 다시 그리고도 30번의 업데이트 사항을 모두 반영할 수 있게 되는 것입니다. 이것이 Virtual DOM의 장점이라 할 수 있습니다.
Diffing Algorithm
React의 Render함수는 jsx문법에 맞는 React Element를 반환합니다. 이때, 상태가 변하거나 프로퍼티가 변해서 DOM을 업데이트해야 하는 경우에 변경된 부분만 감지해서 바뀐 부분만 업데이트 하는 방식을 취하게 되는데, 이 변경된 부분을 감지하는 방법을 Diffing Algorithm이라고 합니다. 여기서 "비교"란 Virtual DOM 끼리의 비교를 의미하며, React는 이전상태와의 비교를 위해 항상 이전 상태의 Virtual DOM의 사본을 유지합니다.
DOM은 트리 구조이기 때문에 변경된 부분을 감지하기 위해서는 트리 구조를 비교해야 합니다. 일반적인 트리 구조의 비교는 O(n^3)의 시간이 소요되는 것으로 알려져 있어 비효율적으로 보이지만, React는 이를 Heuristic한 방식을 사용해서 O(n)의 시간에 해결하고 있습니다. 핵심은 "다 비교하지 말고 상식적으로 필요한 부분만 비교하자" 입니다.
1. 같은 계층
비슷한 컴포넌트는 트리 내에 동일한 계층에 위치할 것이다. 따라서 이 컴포넌트가 갑자기 부모가 바뀌거나 하는 일이 없다고 가정하고 같은 계층에 있는 컴포넌트들끼리만 비교하자
2. key가 있는지 확인하자
하나의 jsx 태그 (react가 지원하는 엘리먼트 태그 방식)은 자바스크립트 객체로 구성되어 있습니다. 이 객체에는 해당 객체가 React의 virtual DOM의 요소임을 확인해주는 symbol값과, 각각의 virtual DOM을 고유하게 구분하는 key값이 들어가게 되며, map등을 통해 생성된 요소들의 경우 고유한 키들이 있습니다. 리액트는 이 키들의 비교를 통해 리스트의 요소가 추가되거나 삭제되었을때, 해당 내용을 빠르게 감지하고 반영할 수 있습니다.
자세한 내용은 리액트 공식문서의 Reconcilation파트를 참조하세요. (자세한 알고리즘은 리액트 공식 라이브러리에서 확인하실 수 있습니다)
'Frontend' 카테고리의 다른 글
[React] 클로저와 useState Hooks (2) | 2020.09.20 |
---|---|
[React] Redux Middleware 직접 구현해보기 (0) | 2020.09.19 |
[React] Redux 직접 만들어보기 (0) | 2020.09.04 |
[HTML] iframe 태그란? (0) | 2020.08.23 |
[Browser] Console 객체 (0) | 2020.07.26 |