updateElement
`updateElement`는 새로운 가상 DOM과 이전 가상 DOM을 비교하여 변경된 부분만 실제 DOM에 반영하는 역할을 한다. 이 과정이 바로 Virtual DOM의 핵심, diffing 알고리즘이다.
1. 새로운 노드와 이전 노드가 모두 없는 경우
if (!newNode && !oldNode) return;
아무 작업도 하지 않는다.
예시
{show && <div>hello</div>}
// show가 false 일 때
// oldNode: undefined
// newNode: undefined
2. 새로운 노드가 없으면 기존 노드를 제거
if (!newNode && oldNode) {
const child = parentElement.childNodes[index];
if (child) parentElement.removeChild(child);
return;
}
가상 DOM에서 해당 요소가 사라지면 실제 DOM에서도 지워야 한다.
왜 index를 쓰나?
서로 비교할 노드를 선택하기 위해 사용한다.
예시
oldNode: [<div>1</div>, <div>2</div>]
newNode: [<div>1</div>]
// newNode에서 두 번쨰 div 사라짐
// index: 1 에 있는 DOM을 제거
// 실제 DOM
<div>1</div>
<div>2</div> // 제거 대상!
3. 기존 노드가 없으면 새로운 노드 추가
if (newNode && !oldNode) {
parentElement.appendChild(createElement(newNode));
return;
}
새로운 노드가 추가돼서 DOM에 새로 붙여야한다.
예시
oldNode: [<div>1</div>]
newNode: [<div>1</div>, <div>2</div>]
// oldNode에 두 번째 div가 없음
// newNode에는 존재함
// index: 1에 DOM 새로 추가
// 실제 DOM
<div>1</div>
<div>2</div> // 새로 추가!
4. 현재 위치의 DOM 에 노드가 없으면 새로 추가
// 기존 자식 노드 가져오기
const $currentNode = parentElement.childNodes[index];
// 현재 노드가 없는 경우 새로운 노드 추가
// index가 밀릴 상황을 대비하여 없으면 새로 추가한다.
if (!$currentNode) {
parentElement.appendChild(createElement(newNode));
return;
}
가상 DOM 상에는 oldNode 있다고 판단했지만, 실제 DOM에는 해당 index 위치에 노드가 없을 수 있다. index 기반 순회가 불안전하거나 이전 렌더에서 잘못 제거된 경우를 대비한 방어 코드이다.
예시
oldChildren: [A, B]
newChildren: [B, C]
// 순서 변경 중 모종의 이유로 index 어긋났다고 가정
// index: 1 위치에 노드가 있어야하지만 노드가 없음
// index: 1 이 없으니까 [C]를 추가
5. 두 노드가 모두 문자열/숫자일 경우 텍스트 비교
if ((typeof newNode === "string" || typeof newNode === "number") &&
(typeof oldNode === "string" || typeof oldNode === "number")) {
if (newNode !== oldNode) {
parentElement.replaceChild(document.createTextNode(newNode), $currentNode);
}
return;
}
두 노드가 모두 텍스트/숫자일 경우 같으면 유지하고 다르면 새로운 텍스트 노드로 교체한다.
예시
oldNode: ["hello"]
newNode: ["Hi"]
// 실제 DOM
// "hello" -> "Hi" 로 교체
6. 타입이 다르면 새 노드로 교체
if (newNode.type !== oldNode.type) {
parentElement.replaceChild(createElement(newNode), $currentNode);
return;
}
`<div>` -> `<span>` 처럼 태그 타입이 다르면 교체한다.
예시
oldNode: <div>Text</div>
newNode: <span>Text</span>
// 실제 DOM
// <div>Text</div>
<span>Text</span> // 새로 교체
7. 속성과 이벤트 갱신
// 기존 자식 노드 가져오기
const $currentNode = parentElement.childNodes[index];
// 새로운 노드와 기존 노드의 props를 확인한다.
const newProps = newNode.props || {};
const oldProps = oldNode.props || {};
updateAttributes($currentNode, newProps, oldProps);
`ClassName`, `checked`, `disabled`, `onClick` 등 속성 변경 사항을 반영한다.
예시
// className
oldNode: <button>Click</button>
newNode: <button className="btn secondary">Click</button>
// checked
oldNode: <input type="checkbox" checked />
newNode: <input type="checkbox" />
// disabled
oldNode: <button disabled></button>
newNode: <button>Click</button>
// 실제 DOM
// className
<button class="btn secondary">Click</button> // className -> class 로 변경
// checked
<input type="checkbox" /> // checked 속성 제거
// disabled
<button>Click</button> // disabled 속성 제거
8. 자식 노드 재귀 비교
const newChildren = newNode.children || [];
const oldChildren = oldNode.children || [];
const maxLength = Math.max(newChildren.length, oldChildren.length);
for (let i = 0; i < maxLength; i++) {
updateElement($currentNode, newChildren[i], oldChildren[i], i);
}
현재 요소의 자식들을 한 개씩 순회하며 재귀적으로 `updateElement` 를 수행한다.
9. 필요 없는 노드 제거
while ($currentNode.childNodes.length > newChildren.length) {
$currentNode.removeChild($currentNode.lastChild);
}
가상 DOM에는 더 이상 자식이 없지만 실제 DOM에는 자식이 남아있는 경우 제거한다.
예시
oldNode: [<div>1</div>, <div>2</div>, <div>3</div>]
newNode: [<div>1</div>]
// 실제 DOM
<div>1</div> // 유지
//<div>1</div> // 삭제
//<div>1</div> // 삭제
// 마지막 자식 2개 제거
마무리
`updateElement`를 구현하면 Virtual DOM이 변경된 부분만 DOM에 반영되도록 만들 수 있다. 이로 인해 불필요한 렌더링이 줄어들고, 성능이 크게 향상된다.
다음 글에서는 이벤트 위임과 SyntheticEvent 구현을 두뤄보자!
'Front > 향해플러스' 카테고리의 다른 글
| Virtual DOM 만들기 - #6 eventManager (향해 플러스 2주차) (1) | 2025.08.12 |
|---|---|
| Virtual DOM 만들기 - #4: renderElement (향해 플러스 2주차) (1) | 2025.08.11 |
| Virtual DOM 만들기 - #3. createElement (향해 플러스 2주차) (2) | 2025.08.10 |
| Virtual DOM 만들기 - #2. normalizeVNode (향해 플러스 2주차) (0) | 2025.08.10 |
| Virtual DOM 만들기 - #1. createVNode (향해 플러스 2주차) (3) | 2025.08.02 |