리액트의 Virtual DOM

silver·2025년 5월 25일

프론트엔드

목록 보기
5/5
post-thumbnail

DOM

Virtual DOM에 대해 설명하기 전에 DOM에 대해 알아보려 한다.
DOM(Document Object Model)은 HTML을 트리 구조의 객체로 표현한 모델이다. 이 DOM에는 API가 있어서 자바스크립트와 같은 프로그래밍 언어로 컨트롤할 수 있다.

웹 페이지를 동적으로 작동시킬 때 이 DOM API를 활용한다. 사용자의 행동에 따라 UI를 변경하거나 데이터가 변할 때 마다 DOM을 조작해서 UI를 업데이트하는 것이다.

DOM 조작 시 브라우저에서 일어나는 작업

DOM을 조작할 때 브라우저는 다음과 같은 과정을 거친다.

  1. DOM트리 수정 - HTML 구조가 변경된다.
  2. 스타일 재계산 - 어떤 요소에 어떤 CSS가 적용되어야 하는지 계산한다.
  3. 레이아웃(reflow) - 요소들의 위치와 크기를 계산한다.
  4. 페인트 - 계산된 스타일로 픽셀 단위로 화면을 그린다.
  5. 합성 - 여러 레이어를 조합해서 최종 화면 출력한다.

이러한 작업은 모두 연산 비용이 높은 작업이다. 많은 요소를 동시에 조작하거나 DOM을 자주 업데이트하는 경우 성능 저하가 발생할 수 있다.

예를 들어 특정 데이터를 표시하는 UI가 있을 때 데이터가 바뀔 때마다 DOM을 조작해야 한다. 이 때 데이터의 양이 많아지거나 데이터가 자주 변경된다면 이 과정은 비효율적이다.

Virtual DOM

리액트는 이러한 문제를 해결하기 위해 Virtual DOM을 도입했다. Virtual DOM은 실제 DOM과 비슷하지만 메모리 상에 존재하는 가상의 DOM 트리이다.

Virtual DOM은 컴포넌트가 렌더링 될 때마다 새로운 Virtual DOM을 생성해서 이전 Virtual DOM과 비교(Diffing)하고 변경사항이 있는 부분만 실제 DOM에 반영한다. 이를 통해 불필요한 DOM 조작을 줄이고 효율적으로 UI를 업데이트할 수 있게 된다.

Virtual DOM이 동작하는 흐름은 다음과 같다.

  1. 상태(state)또는 props가 변경된다.
  2. 해당 컴포넌트의 함수가 다시 실행되어 새로운 Virtual DOM이 생성된다.
  3. 이전 Virtual DOM과 비교(Diffing)하여 변경된 부분을 찾는다.
  4. 변경된 요소에 대해서만 실제 DOM을 업데이트한다.

위 이미지처럼 기존에 [1,2,3,4,5,6] 데이터를 출력하는 요소가 있고 이 데이터가 [1,7,3,8,5,9]로 변경되는 경우를 예로 들어보자.

Virtual DOM을 사용하지 않고 배열의 map을 통해 UI를 리렌더링 하게 될 경우 전체 데이터에 해당하는 UI를 새롭게 렌더링하게 된다.

Virtual DOM을 사용하게 되면 데이터가 변화할 때 컴포넌트 함수가 다시 실행되면서 Virtual DOM을 새로 생성하게 되고 이를 이전 Virtual DOM과 비교한다. 변경사항은 2 -> 7, 4 -> 8, 6 -> 9이기 때문에 실제 DOM에선 이 세개의 노드만 업데이트 한다.

Key를 사용하는 이유

리액트에서 배열을 기반으로 요소를 렌더링할 때 key를 입력하는 이유는 Virtual DOM을 통해 Diffing하는 작업을 최적화하기 위함이다.

key는 각 요소를 고유하게 식별하는 값으로 React가 이전 Virtual DOM과 새로운 Virtual DOM을 비교할 때 어떤 요소가 변경,추가되고 삭제됐는지 빠르게 파악할 수 있도록 도와준다.

만약 key가 입력되지 않거나 배열의 index를 key로 사용하게 되면 중간에 요소가 삽입되거나 삭제될 때 React는 그 이후의 모든 요소가 변경된 것으로 간주한다.

예를 들어 다음과 같은 배열이 있다고 해보자.

const before = [1,2,3,4,5];
const after = [1,2,4,5];

3이 제거된 상황이기 때문에 실제로는 해당 요소만 제거하면 된다. 하지만 index를 Key로 입력하게 되면 아래와 같이 비교하게 된다.

Key를 Index로 입력했을 때

KeyBeforeAfter
011
122
234
345
45X

특정 값이 제거되더라도 인덱스는 변함이 없기 때문에 key는 고정된 상태로 값만 바뀌게 된다.

그러면 리액트는 key가 2인 요소가 삭제된 것이 아니라
key가 2인 요소의 값이 4로 변경되고
key가 3인 요소의 값이 5로 변경되고
key가 4인 요소가 삭제된 것으로 판단한다.

key가 2인 요소만 삭제하면 되는데 이렇게 되면 key가 2,3인 요소들이 리렌더링 되고 key가 4인 요소가 삭제된다.

그렇기 때문에 key에는 index가 아닌 각 데이터의 고유한 값을 입력해줘야 한다.

Key를 Id로 입력했을 때

KeyBeforeAfter
id1{ id :'id1', value : 1 }{ id : 'id1', value : 1 }
id2{ id :'id2', value : 2 }{ id :'id2', value : 2 }
id3{ id :'id3', value : 3 }X
id4{ id :'id4', value : 4 }{ id :'id4', value : 4 }
id5{ id :'id5', value : 5 }{ id :'id5', value : 5 }

Key를 각 요소의 ID로 입력했다면 세번째 요소를 삭제할 경우 key가 id3인 요소가 삭제된 것으로 간주해 id3에 해당하는 요소만 삭제해서 최적화된 DOM 조작이 가능하다.

정리

처음에는 Virtual DOM이 DOM의 성능을 개선한 별도의 새로운 기술이라고 생각했다. 하지만 실제로는 기존의 DOM을 가장 효율적으로 조작하기 위해 만들어진 패턴이란 것을 알게 되었다.

리액트도 결국 자바스크립트로 만들어진 라이브러리이기 때문에 브라우저가 제공하는 DOM API의 한계를 벗어날 수 없다. 그렇기 때문에 브라우저에서 DOM을 조작하는 완전히 새로운 방식을 만들어낸 것이 아니라 기존 기술을 바탕으로 DOM 업데이트를 최적화하는 구조를 만든 것이다.

0개의 댓글