[Javascript] DOM의 변화를 감시하는 MutationObserver 사용하기

2022. 10. 28. 00:17WEB Dev/Javascript | REACT | Node.js

728x90

 

리액트에서는 useEffect처럼 DOM의 변화를 인식해서 재렌더링을 해주는 기능이 있지만 바닐라 자바스크립트에서는 누가 데이터의 변화를 감지하고 있지 않기 때문에 여러가지 방법으로 DOM에 변화를 일으켜줘야 한다. 그럴때 사용하는게 바로 Observer 기능인데 이번에는 회사에서 일을 하면서 Observer을 이용해서 여러가지 기능을 만들게 되었다.
 
 

Observer에는 InterSectionObserverMutationObserver 두 가지가 있는데 자세한 설명은 링크한 MDN을 살펴보면 된다.

 

 

InterSectionObserver는 무한 스크롤을 만드는데 사용했는데 MutationObserver로 어떤 Node가 추가되었을 때 다른 Node의 변화를 주는 기능을 구현하여 간단하게 정리해보려고 한다.

 

 

위에서 말한 것처럼 리액트의 경우 화면의 일부가 변화하는 것을 DOM 전체가 인식할 수 있어서 스크립트가 실행되고 나서도 변화를 감지할 수 있다.

그런데 바닐라 자바스크립트의 경우 함수가 1회 실행되고 나서 함수가 다시 실행되기 위해선 addEventListener와 같은 이벤트를 넣어주거나 함수를 계속 일정하게 재실행시켜주어야 한다.

당연히 후자의 방법은 특별한 경우(시계를 만든다던지)가 아닌 이상 사용하지 않는 방법이고 전자의 방법도 나름 나쁘지 않은 실행방법이라고 생각한다.

 

 

이번에 만든 기능을 간단하게 이야기 하자면 상품 상세페이지에서 옵션을 선택하는데 상품의 총 가격은 숨겨두었다가 옵션 선택을 하면 구매할 상품의 총 가격이 노출되는 기능이다. 옵션 선택은 <select>로 이루어지는데, <select> 옵션 하나를 선택하게 되면 상품 옵션에 대한 Node가 생성되고 아래 구매하는 상품의 총 가격이 뜨는 식이다.

<select> 때문에 onChange로 이벤트를 걸 수도 있지만 내가 사용하고 있는 카페24의 특성 상 <select> 자체가 변수로 감추어져있고, <select> 1개 이상을 선택해야 상품이 선택되는 경우의 수도 있기 때문에 <select> 1개에 이벤트를 건다고 해결되는 문제가 아니었다. 

 

 

내가 찾은 해결책은 <select>에서 상품이 선택되면 생성되는 상품 리스트에 Node가 추가되는 것을 감시하고, 변화가 감지되면 총 가격 Node를 활성화 시키는 방법이었다.

 

 

 

상품을 추가하게 되면 #totalProduct라는 node 하위에 <ul> 아래에 <li>에 상품정보가 추가된다.

따라서 #totalProduct를 변수에 할당한다. 

 

const totalProduct = document.getElementById("totalProducts");

 

이 totalProduct를 Observer로 감시하기 위해 MutationObserver 객체를 선언해준다.

 

const observer = new MutationObserver();

 

그리고 하위 노드를 감시하기 위해 observer 설정을 해주고 totalProduct를 인자로 보내준다.

나의 경우는 totalProduct에 자식 혹은 손자(아주 하위까지) Node들이 추가되는 것을 감시하기 위해 ChildList와 subtree를 사용하였다.

설정하는 값들은 여기를 참고하자 (추가 링크)

 

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

 

이렇게 설정하고 나면 totalProduct에 어떤 변화가 생겼을 때 MutationObserver에서 인식하게 된다.

그 변화를 트리거로 하여 행하고 싶은 기능을 함수로 만들어 new MutationObserver에 콜백함수로 보내주면 된다.

 

내가 원하는 기능은 total_price라는 Node가 보여지는 것이기 때문에 아래와 같은 콜백함수를 작성하였다.

 

// #totalProduct 노드에 하위 옵션값 생성되면 총 합계 노출
// .total_price는 CSS상 display:none 상태
    const callbackFunc = (mutationList, observer) => {
        document.querySelector(".total_price").style.display="block";
    };

 

저 callbackFunc는 아래와 같이 보내주면 된다.

 

const observer = new MutationObserver(callbackFunc);

 

이렇게 되면 #totalProducts에 하위 Node가 추가되는 것을 트리거로 하여 마치 이벤트 리스너를 붙여둔 것처럼 추가할때마다 콜백함수callbackFunc 가 실행되게 된다. 콜백함수 안에 여러가지 분기를 넣어서 다양하게 기능을 만들어낼 수도 있다.

리액트가 아니더라도 화면 Node 값의 변화에 따라 콜백함수를 실행할 수 있는 MutationObserver는 오페라 미니 브라우저를 제외한 대부분의 브라우저에서 지원해주고 있고 iOS 네이티브 웹뷰 앱에서도 문제없이 실행(심지어 IE11까지)되는 것을 확인했다.

 

 

이번 무한스크롤 피드 기능을 다 개발하고 나면 intersectionObserver도 한 번 정리를 해야겠다.

 

 

728x90