MutationObserver - DOM 변화 감지

iskkiri2024년 10월 27일
MutationObserver
MutationObserver - DOM 변화 감지

웹 개발에서는 동적으로 DOM이 변할 때 이를 감지하고 적절히 처리하는 것이 중요합니다. MutationObserver는 DOM 변화(자식 노드 추가/제거, 속성 변경, 텍스트 노드 변경)를 비동기적으로 감지할 수 있는 API입니다. 이번 포스팅에서는 MutationObserver의 기본 사용법과 함께 다양한 옵션에 따른 예제를 통해 동작 방식을 이해해보겠습니다.

 

MutationObserver란?

 

MutationObserverDOM 트리에서 발생하는 변경 사항을 감지하고 처리할 수 있는 API입니다. 주로 다음과 같은 경우에 유용합니다.

 

  • 자식 노드의 추가/제거

     

  • 속성(attribute)의 변경

     

  • 텍스트 노드의 변경

 

기본 사용법

 

아래는 기본적인 MutationObserver 코드입니다. 대상 노드를 감시하고, 변화를 콘솔에 출력합니다.

 

const targetNode = document.getElementById('app'); // 감시할 대상 노드

const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    console.log('변화 감지:', mutation);
  });
});

const config = {
  childList: true, // 자식 노드 추가/제거 감지
  attributes: true, // 속성 변경 감지
  subtree: true, // 모든 하위 노드의 변화도 감지
};

// 감시 시작
observer.observe(targetNode, config);

// 감시 종료
// observer.disconnect();

 

1. 자식노드 추가/제거 감지

 

옵션에 따라 자식 노드 추가/제거 감지의 동작이 달라질 수 있습니다. 

예제 코드 및 실행 결과는 CodeSandbox에서 확인할 수 있습니다.

 

 

옵션 1: childList: true, subtree: false

 

  • 직접적인 자식 노드의 추가/제거만 감지합니다.

  • 하위 트리의 변화(손자 노드 이상)는 감지하지 않습니다.
     

옵션 2: childList: true, subtree: true

 

  • 대상 노드와 모든 하위 트리에서 발생하는 자식 노드의 추가/제거를 감지합니다.

  • 손자 노드 이상의 깊은 DOM 변화까지 추적합니다.

 

<div id="app">
  <button id="add-child">Add Child</button>
  <button id="add-grandchild">Add Grandchild</button>
</div>

 

const targetNode = document.getElementById('app');
const addChildButton = document.getElementById('add-child');
const addGrandchildButton = document.getElementById('add-grandchild');

// MutationObserver 콜백 함수 정의
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    console.log('변화 감지:', mutation);
  });
});

// 옵션 1: 자식 노드의 추가/제거만 감지 (손자 이상은 무시)
observer.observe(targetNode, { childList: true, subtree: false });

addChildButton.addEventListener('click', () => {
  const newChild = document.createElement('div');
  newChild.textContent = 'Child Node';
  targetNode.appendChild(newChild);
});

addGrandchildButton.addEventListener('click', () => {
  const child = targetNode.querySelector('div');
  if (child) {
    const newGrandchild = document.createElement('span');
    newGrandchild.textContent = 'Grandchild Node';
    child.appendChild(newGrandchild);
  } else {
    console.warn('먼저 자식 노드를 추가하세요!');
  }
});

 

 

  • 옵션 1 (childList: true, subtree: false)

 

  1. “Add Child” 버튼 클릭: 콘솔에 자식 노드 추가가 감지됩니다.

 

변화 감지: MutationRecord { type: 'childList', addedNodes: NodeList [div], ... }

 

  1. “Add Grandchild” 버튼 클릭: 아무 변화도 감지되지 않습니다.

 

 

  • 옵션 2 (childList: true, subtree: true)

 

옵션을 다음과 같이 subtree: true로 변경합니다.

 

observer.observe(targetNode, { childList: true, subtree: true });

 

  1. “Add Child” 버튼 클릭: 자식 노드 추가가 감지됩니다.

 

변화 감지: MutationRecord { type: 'childList', addedNodes: NodeList [div], ... }

 

 

  1. “Add Grandchild” 버튼 클릭: 손자 노드 추가도 감지됩니다.

 

변화 감지: MutationRecord { type: 'childList', addedNodes: NodeList [span], ... }

 

 

속성 변경 감지

 

특정 요소의 속성(attribute)이 변경될 때를 감지해보겠습니다. 예를 들어, 클래스가 변경될 때 로그를 출력할 수 있습니다.

예제 코드 및 실행 결과는 CodeSandbox에서 확인할 수 있습니다.

 

 

<div id="app">
  <button id="toggle">Toggle Class</button>
</div>

 

const targetNode = document.getElementById('app');
const toggleButton = document.getElementById('toggle');

const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
      console.log('클래스 변경 감지:', targetNode.className);
    }
  });
});

observer.observe(targetNode, { attributes: true });

toggleButton.addEventListener('click', () => {
  targetNode.classList.toggle('active');
});

 

 

  1. “Toggle Class” 버튼 클릭: <div id="app">의 클래스가 active로 추가되거나 제거됩니다.

  2. 콘솔 출력: 클래스가 변경된 내용이 로그로 출력됩니다.

 

텍스트 노드 변경 감지

 

MutationObserver텍스트 노드의 변경도 감지할 수 있습니다. 예를 들어, 사용자가 버튼을 클릭해 텍스트를 변경하면 이를 감지해 로그를 출력할 수 있습니다.

예제 코드 및 실행 결과는 CodeSandbox에서 확인할 수 있습니다.

 

 

 

<div id="app">
  <p id="text-node">이 텍스트를 변경해 보세요!</p>
  <button id="change-text">텍스트 변경</button>
</div>

 

const targetNode = document.getElementById("text-node").firstChild;
const changeTextButton = document.getElementById("change-text");

// MutationObserver 설정
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.type === "characterData") {
      console.log("텍스트 노드 변경 감지:", mutation.target.textContent);
    }
  });
});

// 텍스트 노드의 변경을 감시하도록 설정
observer.observe(targetNode, { characterData: true });

changeTextButton.addEventListener("click", () => {
  targetNode.textContent = "텍스트가 변경되었습니다!";
});

 

 

  1. “텍스트 변경” 버튼 클릭: <p> 요소의 텍스트가 변경됩니다.

  2. 변경된 텍스트가 콘솔에 출력됩니다.

 

텍스트 노드 변경 감지: 텍스트가 변경되었습니다!

 

정리

 

MutationObserver자식 노드의 추가/제거, 속성 변경, 텍스트 노드의 변경을 비동기적으로 감지할 수 있는 강력한 도구입니다. 특히, childListsubtree 옵션을 적절히 조합하면 복잡한 DOM 트리에서도 정확한 감시가 가능합니다. 이처럼 다양한 기능을 적절히 활용하면 동적 UI를 더 쉽게 관리할 수 있습니다.

 

성능 최적화도 중요한 부분입니다. 불필요한 감시 범위를 줄이고, 필요한 옵션만 설정하면 더 나은 성능을 유지하면서 DOM 변화를 감지할 수 있습니다.

MutationObserver - DOM 변화 감지