리액트 가상 DOM을 사용하는 이유와 동작과정

Zero·2024년 5월 19일
0

[React]

목록 보기
4/6
post-thumbnail

리액트의 가상돔의 구조와 가상돔을 사용하는 이유에 대해 알고계신가요?

언어기능상실

면접때 들었던 질문이다. 해당 질문을 듣고 어이없음을 경험했다. 왜냐면 평소에 자주 사용하는게 React이고 jsx, tsx 평소에 생각하지 않았던 질문입니다.

생각지도 못한 질문에 당황한 나는 다음과 같이 대답을 하였습니다.

가상돔을 사용하는 이유는 직접적으로 돔을 수정하게 될 경우 많은 비용처리가 일어나기 때문에 가상돔과 현재 돔을 비교를 하면서 처리를 하는걸로 알고 있습니다.

위 대답이 무슨 뜻일까? 돔을 처리하기 때문에 비용처리가 일어난다? 가상돔과 현재 돔을 비교를 하면서 처리를 한다? 다시 한번 생각을 해봅니다.


DOM을 직접적으로 수정하게 될 경우 많은 비용처리가 일어난다.

DOM은 서버가 아니다 비용처리가 일어날 수 없다. 무슨 말일까?

DOM은 웹 페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 가지고 있습니다.

사용자가 주소를 치고 웹 페이지에 방문할 경우 HTML 코드를 다운로드 하고, CSS 코드를 받아오고 HTML을 파싱, 레이아웃 계산, 페인팅 등과 같이 복잡한 과정들로 웹 페이지를 그립니다.

언어기능상실

비용처리, 비용이 많이든다 하는 것은 브라우저가 DOM을 추가를 할 때 JavaScript를 통해 DOM을 추가해야하기에 이에맞는 연산 (ex: DOM이 추가되거나 변경이 될 경우 하위 자식 요소도 덩달아 변경돼야 하기 때문에 더 많인 연산을 해야한다.) 이 다시 한번 이루어지게 됩니다.

또한 새로운 DOM이 추가되었으므로 레이아웃에 대해 다시한번 계산을 시도해야하고 페인팅과 합성등을 통해 화면을 다시 그리면서 CPU및 GPU의 리소스를 소모하게 됩니다.

DOM이 변경이 되면서 비용처리가 일어난다는 것은 연산 및 리소스 소모를 의미하는 것입니다.


Virtual DOM 현재 DOM을 비교를 하면서 새롭게 그려준다.

리액트를 조금이라도 공부를 했다면 많이 들었던 얘기일 것이다. 하지만 해당 답변에 대해서는 부족함이 느껴진다. 따라서 꼬리질문이 나오기 좋은 타이밍이다.

그렇다면 어떻게 비교를 하는걸까요? 알고있는 조정 알고리즘이 있을까요?

난 다음과 같이 대답했다.

알고리즘 같은 경우 이전에 관심이 생겨 찾아본적이 있는데 파이버엔진을 사용하여 비교하고 큐에서 트리정도로 바뀌었다 정도로만 알고있습니다.

지금 와서 생각해 보면 개념이 정리가 안 된 상태에서 말을 하려다 보니 과부하가 생겨 정리가 안 된 것 같다. 이 자리를 빌려 다시 한번 짚고 넘어가는 자리가 되면 좋을 것 같다.

알고있는데 말을 못한 것 같아 아쉬움이 남는 대답이었다.

많은걸 보여주려고 한다

답변한 내용 중에 잘못된 내용은 없다. 하나씩 확인해보자.

스택에서 트리로 변경된 알고리즘

과거 리액트는 조정 알고리즘을 스택을 통해 관리를 하였다.

많은걸 보여주려고 한다

리액트는 변경된 것들을 찾아냈고 이러한 것들을 스택에 차곡차곡 쌓아두고 렌더링을 할 준비가 끝나면 또한 스택이 빌 때까지 동기적으로 렌더링을 진행한다.

하지만 문제점이 있었으니 그것은 자바스크립트 싱글스레드 한계로 인해 동기 작업은 중단될 수 없고 다른 작업을 진행하고 싶어도 중단할 수 없는 지경에 이르러 리액트의 비효율성으로 이어졌다.

리액트 팀이 이러한 비효율성을 없애기 위해 스택 조정자 대신 Fiber라는 개념을 만들었다.

Fiber 엔진은 이 가상 DOM의 각 요소(element)에 대응하는 Fiber 노드(FiberNode)를 만듭니다. 그리고 Diff 알고리즘이 찾아낸 변경 사항을 바탕으로 이 Fiber 노드를 효율적으로 업데이트한다.

Diff 알고리즘은 "변경 사항을 찾아내는 역할", Fiber 엔진은 "그 변경 사항을 효율적으로 적용하는 역할"

Diff 알고리즘이 작동하는 방식

해당 글을 보고 참고가 많이 되었습니다.

React16버전부터 Virtual DOM을 트리형태로 관리하기 시작했습니다.

Dom 트리와 Virtual 트리를 비교를 하는 트리와 트리를 비교하는 알고리즘을 리액트 팀에서 만들었습니다.

우리는 보통 이것은 O(n^3)의 시간 복잡도를 가진다고 생각하지만 리액트는 Diff 알고리즘을 적용하면서 해당 복잡도를 O(n)까지 줄였습니다.

방법은 아래와 같습니다.

기존에 얘기한 방식과 같이 모든 DOM 트리와 Virtual DOM 트리를 비교하게 될 경우 O(n^3)의 시간 복잡도를 가집니다. 하지만 리액트팀은 diff 알고리즘의 heuristics 탐색을 함으로써 불필요한 노드를 방문하지 않는 방법을 택합니다.

때문의 방문을 해야하는, 최선의 노드들만 방문을 하기에 O(n)의 시간복잡도를 가질 수 있었습니다.

heuristics 탐색

리액트는 동일한 계층의 노드들끼리만 비교하는 것을 시작으로, 불필요한 노드 방문을 최소화하는 전략을 취합니다. 이를 통해 실제로 변경이 필요한 최소한의 노드들만 선별적으로 방문하게 되는 것입니다.

또한 사용자 인터페이스에서 구성 요소들은 트리의 다른 수준으로 쉽게 이동하지 않고, 주로 자식 노드들끼리만 관계를 맺는 경우가 많습니다. 리액트는 이러한 특성을 잘 활용하여 효율적인 Diff 알고리즘을 구현할 수 있었습니다.

heuristics 탐색

또 반복되어 만들어지는 List 형태의 컴포넌트들이 있다면 각 컴포넌트마다 고유한 key 값을 통해 변경사항등을 파악할 수 있습니다.

key 값이 동일한 경우 props를 비교를 합니다. props가 변경이 되었다면 현재 내부 상태가 변경이 된 것으로 간주합니다.

heuristics 탐색

만약 컴포넌트가 변경이 되었다면 불필요한 비교는 하지 않고 곧바로 변경을 시도하기도 합니다.

예를 들어, <Header> 컴포넌트와 <ExampleBlock> 컴포넌트는 서로 다른 유형의 컴포넌트이므로, React의 Diff 알고리즘은 이들을 매칭시키지 않고 렌더링을 합니다.

참고자료

profile
0에서 시작해, 나만의 길을 만들어가는 개발자.

0개의 댓글