리액트 Virtual DOM에 대해서 간단하게

률루랄라·2022년 7월 12일
1

리액트에서 Virtual DOM은 무엇이고 리액트에서는 왜 이것을 사용하는가에 대해 간략하게 정리해보자


Real DOM

DOM, Document Object Model,은 어플리케이션의 UI를 나타내는 간단한 단어다.
어플리케이션 UI 상태의 변화가 있을 때 마다 DOM은 업데이트되고 변화를 적용하여 나타낸다.
하지만 DOM의 잦은 조작은 어플리케이션 퍼포먼스에 영향을 주어 속도 저하를 야기한다.

무엇 때문에 DOM 조작은 속도 저하를 야기하는가

DOM은 트리 자료 구조로 나타나있다. 그렇기 때문에, DOM의 changes와 updates는 빠르다. 하지만 변화 후, update된 element와 그의 자식들은 어플리케이션 UI를 업데이트하기 위해 re-render되어야한다.
UI의 re-rendering 혹은 re-painting이 바로 속도 저하를 야기한다.
따라서, UI 컴포넌트가 많은수록 모든 DOM 업데이트를 위해 모두 re-render되어야 할 수 있어 DOM 업데이트에 들어가는 비용이 비싸진다 (소요될 작업량이 많아진다로 이해하면 될듯).

Virtual DOM

위의 이슈로 인해 Virtual DOM의 컨셉이 나왔고 실제 DOM보다 월등히 뛰어난 성능을 자랑한다.
Virtual DOM은 그저 단지 DOM의 가상 representation일 뿐이다. 어플리케이션의 상태가 변화할 때 마다, Virtual DOM이 real DOM 대신 업데이트 된다.

간혹 Virtual DOM과 real DOM이 하는 일이 동일해보이기 때문에 double work 하는 것이 아닌지 의문을 갖고 real DOM을 업데이트 하는 것 보다 Virtual DOM이 더 빠른지에 대해 의문을 갖을 수 있다.

왜 Virtual DOM이 실제 DOM보다 빠른지에 대해 알아보자

왜 Virtual DOM이 더 빠른가

UI에 새로운 element가 추가가 되면, tree로 구성된 virtual DOM이 생성된다. 각각 element는 이 트리에서 node이다. 만약 이 elements 중 어느 하나의 element의 상태가 변화하면, 새로운 Virtual DOM 트리가 생성된다. 이 새로운 트리는 이전 Virtual DOM 트리와 diff, 즉 비교가 된다.

비교가 끝난 후, Virtual DOM은 real DOM에 적용할 가장 가능성 높은 (혹은 효율적인) 방법을 계산한다. 이 과정은 실제 DOM 상의 최소한의 작동을 보장한다. 실제 DOM을 업데이트 하는것에 대한 퍼포먼스 비용을 줄이는 것이다.

다음 이미지는 virtual DOM tree와 비교 과정을 보여준다.
virtual DOM and diffing process

빨간색 원은 변화된 노드들을 나타낸다. 이러한 노드들은 그들의 state가 변화된 UI element를 나타낸다.
이전 버전의 Virtual DOM tree와 새로운 버전의 Virtual DOM tree 사이의 차이가 계산된다. 부모의 하위 subtree들은 업데이트된 UI를 표현하기 위해 모두 re-render된다. 그러면 이 업데이트된 트리가 실제 DOM으로 일괄 업데이트 된다.

리액트는 Virtual DOM을 어떻게 사용하는가

리액트에서 모든 UI 조각들은 컴포넌트이며 각 컴포넌트는 상태(state)를 갖는다. 리액트는 observerable pattern을 따르고 모든 상태 변화를 listen한다. 컴포넌트의 상태가 변화하면, 리액트는 Virtual DOM tree를 업데이트 한다. Virtual DOM의 업데이트가 끝나면, 리액트는 현재 버전의 Virtual DOM과 이전 버전의 Virtual DOM을 비교한다. 이 과정을 diffing이라고 부른다.

리액트가 어떤 Virtual DOM object가 변화했는지 알게되면, 리액트는 그 변화된 objects만 실제 DOM에 업데이트한다. 이는 실제 DOM을 직접 조정(manipulating) 하는 것과 비교하여 훨신 나은 퍼포먼스를 만들어 낸다. 이 것이 리액트를 high perfomance javascript library로 돋보이게 하는 것이다.

다시 말해, 개발자는 단지 UI가 어떤 상태로 변화되길 리액트에 말하면, 리액트는 그 상태와 DOM이 일치하도록 보장한다. 이 때문에 개발자들은 리액트 내부에서 속성 변화, 이벤트 핸들링 또는 수동 DOM 업데이트가 어떻게 작동되는지 알 필요없게 된다.

이 모든 세부 정보들은 리액트 개발자로 부터 추상화된다. 개발자가 해야할 일은 필요할 때에 맞게 컴포넌트의 상태를 변화시켜주면 나머지는 리액트가 알아서 처리해준다. 이는 리액트를 사용함에 있어 뛰어난 dx (developer experience)를 경험하게 해준다.

render() 함수

render() 함수는 UI가 업데이트되고 렌더되는 곳이다. render() 함수는 리액트에서 필수적인 lifecycle method다.

render() 함수는 리액트 elements의 트리가 생성되는 엔트리 포인트다. 컴포넌트의 state나 props가 업데이트되면 render() 함수는 리액트 element의 다른 tree를 반환한다. 컴포넌트 내부에서 setState()가 사용되면, 리액트는 상태 변화를 즉시 감지하고 컴포넌트를 re-render한다.

그 후 리액트는 변화가 적용된 가장 최신의 트리에 해당하는 UI로 가장 효율적으로 업데이트하는 방법을 계산한다.

이 떄가 리액트가 먼저 virtual DOM을 업데이트 하고 실제 DOM에서 변화된 objects만 업데이트 하는 떄이다.

Batch Update

리액트는 실제 DOM을 업데이트 하기 위해 batch update mechanism을 따른다.
각 상태 업데이트에 대한 operation을 보내는 대신 실제 DOM에 대한 업데이트를 일괄적으로 보낸다.

UI 리페인팅이 가장 비싼 부분이고 리액트는 UI 리페인트 하기 위해 오직 batched 업데이트들만 실제 DOM이 받을 수 있도록 효율적으로 확신한다.

참고: https://velopert.com/3236

profile
💻 소프트웨어 엔지니어를 꿈꾸는 개발 신생아👶

0개의 댓글