한양과학기술고등학교

에듀테크 프로젝트 대시보드

가상 DOM & 재조정

가상 DOM & 재조정(Reconciliation)

React 같은 라이브러리는 화면 전체를 다시 그리는 것처럼 코드를 짜도, 실제로는 바뀐 부분만 콕 집어 고친다. 그 비밀이 가상 DOM과 diff 알고리즘이다.

1. 실제 DOM은 왜 느릴까

브라우저의 DOM은 노드 하나를 만들거나 속성을 바꿀 때마다 스타일 계산·레이아웃·페인트가 연쇄적으로 일어날 수 있는 무거운 객체다. 목록 100개 중 1개만 바뀌었는데 innerHTML로 전부 새로 그리면, 브라우저는 99개의 멀쩡한 노드까지 부수고 다시 만든다. 입력 포커스·스크롤 위치·애니메이션 상태도 함께 날아간다.

그래서 "바뀐 곳만 찾아서 그 부분만 DOM API를 호출"하는 게 핵심이다. 문제는 — 손으로 그 코드를 짜면 복잡하고 버그가 많다는 것.

2. 가상 DOM = 화면을 표현한 가벼운 JS 객체

가상 DOM(VNode)은 그냥 평범한 자바스크립트 객체 트리다. 만들고 버리는 비용이 거의 없다.

// <li class="done">우유 사기</li> 를 표현한 VNode
{
  type: "li",
  props: { class: "done", key: 3 },
  children: [ "우유 사기" ]
}

UI 함수는 현재 상태(state)를 받아 VNode 트리를 통째로 반환한다. 상태가 바뀌면 새 트리를 다시 통째로 만든다. 여기까지는 비싸지 않다 — 진짜 DOM은 아직 안 건드렸으니까.

3. diff & patch — 두 트리를 비교해 최소 수술 목록 만들기

이전 VNode 트리(old)와 새 VNode 트리(new)를 같은 위치끼리 짝지어 내려가며 비교한다.

비교 결과수행할 DOM 작업
old 없음 / new 있음CREATE — 노드 생성 후 삽입
old 있음 / new 없음REMOVE — 노드 제거
타입이 다름 (div↔span)REPLACE — 통째로 교체
둘 다 텍스트인데 내용이 다름TEXT — nodeValue만 갱신
타입 같음, 속성만 다름PROPS — 바뀐 속성만 set/remove
타입 같음→ 자식들을 재귀적으로 다시 diff

비교가 끝나면 "패치 목록"이 남고, 이걸 실제 DOM에 한 번에 적용한다. 아무것도 안 바뀐 노드는 건드리지 않는다.

4. 직접 해보기 — 할 일 목록 재조정

버튼으로 상태를 바꾸면, 아래에서 (1) 실제로 다시 그려지는 DOM, (2) 새 VNode 트리(바뀐 노드 하이라이트), (3) diff가 만들어낸 패치 목록을 볼 수 있다.

① 실제 DOM (다시 그려진 부분만 색으로 반짝)

② 새 VNode 트리

③ diff가 만든 패치 목록

    CREATE REMOVE / REPLACE TEXT PROPS MOVE(key)

    5. key가 왜 중요한가

    "key 사용"을 끄고 맨 앞에 추가나 순서 섞기를 눌러 보자. key가 없으면 diff는 자식을 인덱스(위치) 순서로만 짝짓는다. 맨 앞에 하나 끼워 넣으면 1번은 2번 자리로, 2번은 3번 자리로… 전부 한 칸씩 밀려서 모든 항목의 텍스트가 바뀐 것으로 인식된다. 패치가 잔뜩 생긴다.

    반대로 각 항목에 고유 key가 있으면, diff는 "3번 key를 가진 노드는 이전에도 있었고 내용도 같네 → DOM 노드 재사용, 위치만 이동(MOVE)"이라고 판단한다. 패치가 1~2개로 줄어든다. 리스트를 렌더링할 때 key를 꼭 넣으라는 이유가 이것이다.

    주의 — key로 배열 인덱스(0,1,2…)를 쓰면 순서가 바뀔 때 결국 인덱스 매칭과 똑같아진다. key는 데이터 자체의 고유 ID를 써야 한다.

    6. diff 알고리즘의 복잡도

    두 트리의 "최소 편집 거리"를 엄밀하게 구하는 범용 알고리즘은 O(n³)이라 실전에선 못 쓴다. 그래서 라이브러리들은 몇 가지 휴리스틱으로 O(n)까지 낮춘다.

    • 타입이 다르면 그 아래는 비교하지 않고 통째로 교체한다 (개발자는 보통 노드 타입을 안 바꾼다).
    • 같은 레벨의 자식끼리만 비교한다 — 다른 레벨로의 이동은 "삭제 후 생성"으로 처리한다.
    • key로 같은 레벨 안에서의 이동·재사용을 빠르게 매칭한다.

    완벽하진 않지만 "거의 항상 충분히 좋은" 결과를, 사람이 손으로 짠 것보다 안전하게 만들어 준다.

    한 줄 요약 — 가상 DOM은 화면을 가벼운 JS 객체로 그려 두고, 이전 버전과 diff 해서 바뀐 곳만 진짜 DOM에 반영하는 기법이다. 리스트엔 반드시 데이터 고유 key를 붙이자.