MutationObserver - DOM 변화 감지
iskkiri • 2024년 10월 27일
웹 개발에서는 동적으로 DOM이 변할 때 이를 감지하고 적절히 처리하는 것이 중요합니다. MutationObserver는 DOM 변화(자식 노드 추가/제거, 속성 변경, 텍스트 노드 변경)를 비동기적으로 감지할 수 있는 API입니다. 이번 포스팅에서는 MutationObserver의 기본 사용법과 함께 다양한 옵션에 따른 예제를 통해 동작 방식을 이해해보겠습니다.
MutationObserver란?
MutationObserver는 DOM 트리에서 발생하는 변경 사항을 감지하고 처리할 수 있는 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)
“Add Child” 버튼 클릭: 콘솔에 자식 노드 추가가 감지됩니다.
변화 감지: MutationRecord { type: 'childList', addedNodes: NodeList [div], ... }
- “Add Grandchild” 버튼 클릭: 아무 변화도 감지되지 않습니다.
- 옵션 2 (childList: true, subtree: true)
옵션을 다음과 같이 subtree: true로 변경합니다.
observer.observe(targetNode, { childList: true, subtree: true });
“Add Child” 버튼 클릭: 자식 노드 추가가 감지됩니다.
변화 감지: MutationRecord { type: 'childList', addedNodes: NodeList [div], ... }
“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');
});
“Toggle Class” 버튼 클릭: <div id="app">의 클래스가 active로 추가되거나 제거됩니다.
콘솔 출력: 클래스가 변경된 내용이 로그로 출력됩니다.
텍스트 노드 변경 감지
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 = "텍스트가 변경되었습니다!";
});
“텍스트 변경” 버튼 클릭: <p> 요소의 텍스트가 변경됩니다.
변경된 텍스트가 콘솔에 출력됩니다.
텍스트 노드 변경 감지: 텍스트가 변경되었습니다!
정리
MutationObserver는 자식 노드의 추가/제거, 속성 변경, 텍스트 노드의 변경을 비동기적으로 감지할 수 있는 강력한 도구입니다. 특히, childList와 subtree 옵션을 적절히 조합하면 복잡한 DOM 트리에서도 정확한 감시가 가능합니다. 이처럼 다양한 기능을 적절히 활용하면 동적 UI를 더 쉽게 관리할 수 있습니다.
성능 최적화도 중요한 부분입니다. 불필요한 감시 범위를 줄이고, 필요한 옵션만 설정하면 더 나은 성능을 유지하면서 DOM 변화를 감지할 수 있습니다.