몽키 패치(Monkey Patching)는 기존에 존재하는 객체나 라이브러리의 메서드를 직접 수정하여 새로운 기능을 추가하거나 기존 동작을 변경하는 기법이다.
아래 코드에서는 Node.prototype.removeChild와 Node.prototype.insertBefore의 기본 동작을 변경한다. 이 방식은 강력하지만, 원래 기능을 예상치 못한 방식으로 변경할 수 있어 유지보수가 어려워지는 단점을 가진다.
https://github.com/facebook/react/issues/11538#issuecomment-417504600
🖥️ js
if (typeof Node === 'function' && Node.prototype) {
const originalRemoveChild = Node.prototype.removeChild;
Node.prototype.removeChild = function(child) {
if (child.parentNode !== this) {
if (console) {
console.error('Cannot remove a child from a different parent', child, this);
}
return child;
}
return originalRemoveChild.apply(this, arguments);
}
const originalInsertBefore = Node.prototype.insertBefore;
Node.prototype.insertBefore = function(newNode, referenceNode) {
if (referenceNode && referenceNode.parentNode !== this) {
if (console) {
console.error('Cannot insert before a reference node from a different parent', referenceNode, this);
}
return newNode;
}
return originalInsertBefore.apply(this, arguments);
}
}
🖥️ js
if (typeof Node === 'function' && Node.prototype) {
🖥️ js
const originalRemoveChild = Node.prototype.removeChild;
→ 이렇게 하면, 나중에 원래 기능을 유지하면서 변경 가능.
🖥️ js
const originalInsertBefore = Node.prototype.insertBefore;
🖥️ js
Node.prototype.removeChild = function(child) {
Node.prototype.removeChild를 덮어써서 새로운 동작을 정의
🖥️ js
if (child.parentNode !== this) {
if (console) {
console.error('Cannot remove a child from a different parent', child, this);
}
return child;
}
🖥️ js
return originalRemoveChild.apply(this, arguments);
🖥️ js
Node.prototype.insertBefore = function(newNode, referenceNode) {
🖥️ js
if (referenceNode && referenceNode.parentNode !== this) {
if (console) {
console.error('Cannot insert before a reference node from a different parent', referenceNode, this);
}
return newNode;
}
referenceNode가 존재하면서, referenceNode.parentNode가 현재 노드(this)가 아니면 오류 메시지를 출력한다.
즉, 다른 부모 노드에 있는 referenceNode 앞에 삽입하려 하면 막는 것.
🖥️ js
return originalInsertBefore.apply(this, arguments);
원래의 insertBefore를 그대로 실행해서 정상적인 경우에는 동작이 바뀌지 않도록 한다.
Google Translate 확장 프로그램은 웹사이트의 DOM을 직접 조작해서 번역된 내용을 삽입하는데,
이 과정에서 removeChild나 insertBefore 같은 메서드가 엉뚱한 부모 노드를 대상으로 실행될 수 있다.
예를 들어
🖥️ js
someParent.removeChild(someChild); // someChild의 부모가 someParent가 아닐 수도 있음
이런 문제의 경우 브라우저 콘솔에 Uncaught DOMException 같은 에러 발생.
위 패치 코드는