[250321] TIL

오늘 한 일

Virtual DOM 이란?

  • 실제 DOM 을 추상화한 개념
  • 메모리 내에 가상으로 존재하는 DOM을 의미
  • 실제 DOM 에 변화는 리플레이 리프엔트에 대한 비용이 많이 발생함
  • 버츄얼 돔에 먼저 변화를 처리하고, 이 변경 사항을 적용하기 전에 이전 버츄얼 돔과 비교하여 필요한 부분만 찾아내 실제 DOM 에 적용함 ( Diffing, Reconciliation )

리액트에서 Virtual DOM

  • 리액트에서 핵심 기능 중 하나임
  • 리액트는 렌더링이 발생될 상황에 놓이게 되면 새로운 화면에 들어갈 내용이 담긴 가상 돔을 생성
  • 리액트는 이전 화면 구조와 렌더링 이후 화면 구조 2개의 가상 돔 객체 유지

diffing 알고리즘 동작 방식

  1. 이전 가상돔 트리와 새로운 가상돔 트리를 비교하고 루트 노드에서 시작해서 이전과 새로운 노드를 비교
  2. 두 노드가 다른 유형이면 새 노드를 생성하여 기존 노드를 대체
  3. 두 노드가 같은 유형이면 속성을 비교해서 변경된 것이 있는지 확인함
    1. 변경 된 속성이 있으면 속성 업데이트
    2. 변경 된 속성이 없으면 그대로 사용
  4. 리스트 항목의 경우 key를 사용해서 효율적으로 비교s
  5. 자식노드를 재귀적으로 비교

Reconciliation 단계 (재조정)

  • diffing 알고리즘을 포함한 더 넓은 개념
  • Virtual DOM의 변경사항을 실제 DOM에 효율적으로 적용하는 전체 프로세스를 의미
  • Diffing에서 식별된 변경사항을 바탕으로 실제 DOM 업데이트 계획을 수립
  • 변경사항을 배치(batch)로 그룹화하여 처리
  • 실제 DOM 업데이트를 최적화하여 수행
  • 컴포넌트 생명주기 메서드를 적절한 시점에 호출
  • 이벤트 핸들러 재설정

Vanilla JS 로 Virtual DOM 구현 시 고려사항

1. DOM 표현 구조 설계

  • 노드 표현 방식
    • 각 DOM 노드를 JS 객체로 표현하는 방법 설계
  • 속성 및 이벤트 처리
    • 요소의 속성, 스타일, 이벤트 리스너 등을 어떻게 표현할지 결정
  • 텍스트 노드 처리
    • 텍스트 노드를 어떻게 표현할지 결정
// 가상 DOM 노드 예시
{
  type: 'div',
  props: {
    id: 'container',
    className: 'wrapper',
    onClick: () => console.log('clicked')
  },
  children: [
    { type: 'h1', props: {}, children: ['제목'] },
    { type: 'p', props: {}, children: ['내용'] }
  ]
}

2. Reconciliation 알고리즘

  • 차이점 감지 ( Diffing )
    • 이전 가상 DOM과 새 가상 DOM 간의 차이를 효율적으로 찾는 알고리즘 구현
  • 키 (Key) 관리
    • 리스트 항목의 효율적인 재사용과 재정렬을 위한 고유 식별자 관리
  • 트리 비교 최적화
    • 전체 트리를 비교하는 대신 변경 가능성이 있는 부분만 비교하는 전략

3. DOM 업데이트 최적화

  • 최소 조작 : 필요한 DOM 조작을 최소화하는 방법
  • 배치 처리 : 여러 DOM 업데이트를 그룹화하여 한 번에 처리
  • 비동기 업데이트 : 렌더링 성능을 위해 DOM 업데이트를 비동기적으로 처리

4. 컴포넌트 시스템 설계

  • 컴포넌트 생명주기 : 마운트, 업데이트, 언마운트 등의 생명주기 이벤트 관리
  • 상태 관리 : 컴포넌트 상태를 관리하고 변경 시 리렌더링 트리거 방법
  • 속성 (Props) 전달 : 부모에서 자식 컴포넌트로 데이터를 전달하는 매커니즘

5. 이벤트 처리

  • 이벤트 위임 ( Event Delegation ) : 효율적인 이벤트 처리를 위한 이벤트 위임 구현
    • 여러 요소에 각각 이벤트 리스너를 붙이는 대신, 공통 부모 요소에 하나의 이벤트 리스너를 붙이는 기법
    • 이벤트가 버블링되는 특성을 활용한 패턴
      • 버블링 : DOM 트리를 타고 위로 올라가는 이벤트 버블링
      // 🚫 이벤트 위임 없이 - 각 버튼마다 리스너 추가
      document.querySelectorAll('.button').forEach(button => {
        button.addEventListener('click', function() {
          console.log('버튼 클릭됨:', this.textContent);
        });
      });
        
      // ✅ 이벤트 위임 사용 - 부모 요소에 하나의 리스너만 추가
      document.querySelector('.button-container').addEventListener('click', function(e) {
        // 클릭된 요소가 버튼인지 확인
        if (e.target.classList.contains('button')) {
          console.log('버튼 클릭됨:', e.target.textContent);
        }
      });
    
  • 합성 이벤트 ( Synthetic Events ) : 브라우저 간 일관된 이벤트 처리를 위한 이벤트 래퍼

6. 성능 고려사항

  • 메모리 관리 : 가비지 컬렉션과 메모리 누수 방지
  • 렌더링 최적화 : 불필요한 리렌더링 방지
  • 큰 리스트 처리 : 대량의 데이터를 효율적으로 렌더링하는 방법
// 가상 DOM 노드 생성 함수
function createElement(type, props, ...children) {
  return {
    type,
    props: props || {},
    children: children.flat().map(child => 
      typeof child === 'string' || typeof child === 'number'
        ? { type: 'TEXT_ELEMENT', props: { nodeValue: child } }
        : child
    )
  };
}

// DOM 노드 생성 함수
function createDomNode(vNode) {
  if (vNode.type === 'TEXT_ELEMENT') {
    return document.createTextNode(vNode.props.nodeValue);
  }
  
  const domNode = document.createElement(vNode.type);
  
  // 속성 설정
  Object.keys(vNode.props)
    .filter(key => key !== 'children')
    .forEach(name => {
      if (name.startsWith('on')) {
        const eventType = name.toLowerCase().substring(2);
        domNode.addEventListener(eventType, vNode.props[name]);
      } else {
        domNode[name] = vNode.props[name];
      }
    });
  
  // 자식 노드 처리
  vNode.children.forEach(childVNode => {
    domNode.appendChild(createDomNode(childVNode));
  });
  
  return domNode;
}

// 차이점 비교 및 DOM 업데이트 함수
function updateDom(dom, oldVNode, newVNode) {
  // 구현 생략 - 속성 비교 및 업데이트 로직
  // 자식 노드 비교 및 업데이트 로직
}

// 렌더링 함수
function render(vNode, container) {
  // 초기 렌더링 또는 업데이트 로직
  container.innerHTML = '';
  container.appendChild(createDomNode(vNode));
}

재조정 (Reconciliation) – React

Preserving and Resetting State – React

Categories:

Updated:

Leave a comment