[Observer API ํŒŒํ—ค์น˜๊ธฐ] 4. MutationObserver

โฐ 2024-06-24 (์›”) 02:18:32

screener
์‹œ๋ฆฌ์ฆˆ ๋ชจ์•„๋ณด๊ธฐ
Observer API ํŒŒํ—ค์น˜๊ธฐ

4 / 6

Table of Contents


MutationObserver

MutationObserver๋Š” DOM์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์˜ต์ €๋ฒ„๋‹ค. DOM๊ณผ ๊ด€๋ จ๋œ ์–ด๋–ค ์š”์†Œ๊ฐ€ ๋ณ€ํ•˜๋“ ์ง€ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผํ…Œ๋ฉด, width ํ˜น์€ padding ๊ฐ™์€ style ํƒœ๊ทธ๋Š” ๋ฌผ๋ก , class๋‚˜ id, ์ž์‹ ๋…ธ๋“œ์˜ ์ถ”๊ฐ€ ๋“ฑ์ด ์žˆ๋‹ค.

DOM์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค๋Š” ํŠน์ง•์œผ๋กœ ์ธํ•ด, DOM์˜ ์‚ฌ์ด์ฆˆ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ResizeObserver๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.

TYPESCRIPT

1
2
3
const mo = new MutationObserver(callback);

mo.observe(tag, options);

options

์š”์†Œ ๋“ฑ๋ก ์‹œ options๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์„ธ๋ถ€ ์˜ต์…˜์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. MutationObserverInit ํƒ€์ž…์„ ๊ฐ€์ง„๋‹ค.

์†์„ฑ ํ•„ํ„ฐ (attributeFilter)

attributeFilter์— ์›ํ•˜๋Š” ์†์„ฑ๋ช…์„ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•˜๋ฉด, ํ•ด๋‹น ์ด๋ฆ„์„ ๊ฐ€์ง„ ์†์„ฑ์˜ ๋ณ€ํ™”๋งŒ์„ ํ•„ํ„ฐ๋งํ•˜์—ฌ ๊ฐ์ง€ํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	attributeFilter: [ 'id', 'class' ]
}

์œ„์™€ ๊ฐ™์ด ์ง€์ •ํ•  ๊ฒฝ์šฐ, id ๋ฐ class๊ฐ€ ๋ณ€ํ• ๋•Œ๋งŒ ์˜ต์ €๋ฒ„๊ฐ€ ๊ฐ์ง€ํ•œ๋‹ค. ๋งŒ์•ฝ ์•„๋ฌด ๊ฐ’๋„ ํ• ๋‹นํ•˜์ง€ ์•Š์€ undefined์ผ ๊ฒฝ์šฐ, ๋ชจ๋“  ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค.

์ด์ „ ์†์„ฑ๊ฐ’ (attributeOldValue)

MutationObserver๋Š” DOM์ด ๋ณ€ํ™”๋์„ ๋•Œ ๋™์ž‘ํ•œ๋‹ค. ์ด ๋•Œ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋ณ€ํ™” ์ด์ „์˜ ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋กœ์ง์„ ๊ตฌ์„ฑํ•˜๊ธฐ๋„ ํ•˜๋Š”๋ฐ, ์ด ๋•Œ attributeOldValue ์„ค์ •์ด ์œ ์šฉํ•  ๊ฒƒ์ด๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	attributeOldValue: true
}

์œ„์™€ ๊ฐ™์ด ์ง€์ •ํ•  ๊ฒฝ์šฐ, ์†์„ฑ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•  ์‹œ, ๋ณ€ํ™” ์ด์ „์˜ ๊ฐ’๋„ ๊ฐ™์ด ์ œ๊ณตํ•ด์ค€๋‹ค. ์ด์ „ ์†์„ฑ์ด ์กด์žฌํ•  ์ˆ˜ ์—†๋Š” ์ฒซ ๋ณ€ํ™”๊ฑฐ๋‚˜, ์†์„ฑ์ด ์•„๋‹Œ ๋ณ€ํ™”(์ž์‹ ๋…ธ๋“œ์˜ ์ถ”๊ฐ€ ๋“ฑ)์ผ ๊ฒฝ์šฐ null์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

์†์„ฑ ๋ณ€ํ™” ๊ฐ์ง€ ์—ฌ๋ถ€ (attributes)

DOM ์†์„ฑ ๋ณ€ํ™”์˜ ๊ฐ์ง€ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	attributes: true
}

id, class, style ๊ฐ™์€ DOM์˜ ์†์„ฑ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ, ํ•ด๋‹น ์„ค์ •์„ true๋กœ ์ง€์ •ํ•ด์ค€๋‹ค.

attributeFilter ํ˜น์€ attributeOldValue์— ๊ฐ’์ด ์ง€์ •๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ์˜ต์…˜์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ…์ŠคํŠธ ๋…ธ๋“œ ๋ณ€ํ™” ๊ฐ์ง€ ์—ฌ๋ถ€ (characterData)

DOM์˜ ํ…์ŠคํŠธ ๋…ธ๋“œ ๋ณ€ํ™”(node.value)์˜ ๊ฐ์ง€ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	characterData: true
}

DOM์˜ textContent ๊ฐ’์ด ๋ฐ”๋€” ๊ฒฝ์šฐ, ์ด๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
5
6
7
function handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
	const tag = document.getElementById('target');

	if (tag?.firstChild?.textContent) {
		tag.firstChild.textContent = e.currentTarget.value;
	}
}

์œ„์™€ ๊ฐ™์ด tag.firstChild.textContent ๊ฐ’์ด ๋ณ€ํ•  ๊ฒฝ์šฐ, characterData ์ด๋ฒคํŠธ๊ฐ€ ๊ฐ์ง€๋œ๋‹ค.

์ด์ „ ํ…์ŠคํŠธ ๋…ธ๋“œ ๊ฐ’ (characterDataOldValue)

true๋กœ ์ง€์ •ํ•  ๊ฒฝ์šฐ, attributeOldValue์™€ ๋น„์Šทํ•˜๊ฒŒ ํ…์ŠคํŠธ ๋…ธ๋“œ ๊ฐ’ ๋ณ€ํ™” ์‹œ, ๋ณ€ํ™” ์ด์ „์˜ ๊ฐ’์„ ๊ฐ™์ด ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	characterDataOldValue: true
}

์ด์ „ ์†์„ฑ์ด ์กด์žฌํ•  ์ˆ˜ ์—†๋Š” ์ฒซ ๋ณ€ํ™”๊ฑฐ๋‚˜, ์†์„ฑ์ด ์•„๋‹Œ ๋ณ€ํ™”(์ž์‹ ๋…ธ๋“œ์˜ ์ถ”๊ฐ€ ๋“ฑ)์ผ ๊ฒฝ์šฐ null์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

์ž์‹ ๋…ธ๋“œ ๋ณ€ํ™” ๊ฐ์ง€ ์—ฌ๋ถ€ (childList)

๋Œ€์ƒ DOM์— ์ž์‹ ๋…ธ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ์‚ญ์ œ๋  ๊ฒฝ์šฐ๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	childList: true
}

์œ„์™€ ๊ฐ™์€ ์„ค์ •์„ ํ†ตํ•ด, ํŠน์ • DOM์˜ ๊ตฌ์กฐ ๋ณ€ํ™”๋ฅผ ๋Šฅ๋™์ ์œผ๋กœ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์œ„ ๋…ธ๋“œ ๋ณ€ํ™” ๊ฐ์ง€ ์—ฌ๋ถ€ (subtree)

subtree ์˜ต์…˜์œผ๋กœ ๊ฐ์ง€ ๋ฒ”์œ„๋ฅผ ํ•˜์œ„ ๋…ธ๋“œ๊นŒ์ง€ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

TYPESCRIPT

1
2
3
4
{
	// ...
	subtree: true
}

์œ„ ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด, ํ•˜์œ„ ๋…ธ๋“œ์˜ attributes, characterData, childList ๋ณ€ํ™”๊นŒ์ง€ ๊ฐ์ง€ํ•ด์ค€๋‹ค. ๊ฐ์ง€ ๊ธฐ์ค€์€ ์˜ต์…˜์— ํ• ๋‹นํ•œ ๋ถ€๋ชจ์˜ ๊ธฐ์ค€๊ณผ ๋™์ผํ•˜๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด, attributes ๊ฐ์ง€๋งŒ ํ™œ์„ฑํ™”ํ•˜๊ณ  subtree๋ฅผ ์ ์šฉํ•˜๋ฉด, ์ž์‹  ๋ฐ ํ•˜์œ„ ๋…ธ๋“œ ์ „์ฒด์˜ ์†์„ฑ ๋ณ€ํ™” ์—ฌ๋ถ€๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

callback

์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด DOM ๋ณ€ํ™” ์‹œ ๋™์ž‘ํ•  ๋กœ์ง์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. MutationRecord ํƒ€์ž…์„ ๊ฐ€์ง„๋‹ค. ์ด๋ฅผ ์ฝ”๋“œ๋กœ ํ‘œ๊ธฐํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

TYPESCRIPT

1
const mo = new MutationObserver((record) => {});

์ถ”๊ฐ€๋œ ๋…ธ๋“œ (addedNodes)

addedNodes๋Š” ์ถ”๊ฐ€๋œ ๋…ธ๋“œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์ด๋ฅผ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋…ธ๋“œ ์ถ”๊ฐ€, ์‚ญ์ œ์™€ ๊ด€๋ จ๋œ childList ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๋œ๋‹ค.

HTML

1
2
3
4
5
6
7
8
9
<!-- ์ดˆ๊ธฐ๊ฐ’ -->
<div id="target">
	
</div>

<!-- addedNodes ๋ฐ˜ํ™˜๋จ -->
<div id="target">
	<p>just added!</p>
</div>

์œ„ ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ, #target ํ•˜์œ„์— ์ถ”๊ฐ€๋œ ๋…ธ๋“œ p์˜ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด๋‹ค.

์‚ญ์ œ๋œ ๋…ธ๋“œ (removedNodes)

removedNodes๋Š” ์‚ญ์ œ๋œ ๋…ธ๋“œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์ด๋ฅผ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์กฐ๊ฑด์€ addedNodes์™€ ๊ฐ™๋‹ค.

HTML

1
2
3
4
5
6
7
8
9
<!-- ์ดˆ๊ธฐ๊ฐ’ -->
<div id="target">
	<p>just added!</p>
</div>

<!-- removedNodes ๋ฐ˜ํ™˜๋จ -->
<div id="target">
	
</div>

๋ณ€๊ฒฝ๋œ ์†์„ฑ๋ช… (attributeName)

attributeName๋Š” ์†์„ฑ์ด ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ, ๋ณ€๊ฒฝ๋œ ์†์„ฑ์˜ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์†์„ฑ์˜ ๋ณ€ํ™”์™€ ๊ด€๋ จ๋œ attributes ์˜ต์…˜์ด ํ™œ์„ฑํ™” ๋œ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๋œ๋‹ค.

HTML

1
2
3
4
5
6
7
8
9
<!-- ์ดˆ๊ธฐ๊ฐ’ -->
<div id="target">
	
</div>

<!-- attributeName ๋ฐ˜ํ™˜๋จ -->
<div id="target" class="hi">
	
</div>

์œ„ ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ, #target ์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€๋œ ์†์„ฑ๋ช… class๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

๋ณ€๊ฒฝ๋œ ์†์„ฑ ๋„ค์ž„์ŠคํŽ˜์ด์Šค (attributeNamespace)

attributeNamespace ์†์„ฑ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ, ๋ณ€๊ฒฝ๋œ ์†์„ฑ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, attributes ์˜ต์…˜์ด ํ™œ์„ฑํ™” ๋œ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๋œ๋‹ค.

๋ณ€๊ฒฝ๋œ ์†์„ฑ์ด ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๊ฒฝ์šฐ, ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๊ฐ™์ด ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

HTML

1
2
3
4
5
6
7
8
9
<!-- ์ดˆ๊ธฐ๊ฐ’ -->
<div id="target">
	
</div>

<!-- attributeName ๋ฐ˜ํ™˜๋จ -->
<div id="target" class="hi">
	
</div>

๋งŒ์•ฝ class ์ƒ์„ฑ ์‹œ setAttributeNS๋ฅผ ํ†ตํ•ด ns๋ผ๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋กœ ๋ฌถ์–ด ์ƒ์„ฑํ•œ๋‹ค๋ฉด, ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑ๋œ๋‹ค.

TYPESCRIPT

1
tag.setAttributeNS("ns", "class", "hi");

์œ„์™€ ๊ฐ™์€ ์˜ˆ์‹œ์—์„œ, attributeName์€ class๋ฅผ, attributeNamespace์€ ns๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.

๋ณ€๊ฒฝ๋œ ํƒœ๊ทธ์˜ ๋‹ค์Œ ํ˜•์ œ ํƒœ๊ทธ (nextSibling)

์ถ”๊ฐ€ ํ˜น์€ ์ œ๊ฑฐ๋œ ๋…ธ๋“œ์˜ ๋‹ค์Œ ํ˜•์ œ DOM์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋…ธ๋“œ ์ถ”๊ฐ€, ์‚ญ์ œ์™€ ๊ด€๋ จ๋œ childList ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ฐ˜ํ™˜๋œ๋‹ค.

๋ณ€๊ฒฝ๋œ ํƒœ๊ทธ์˜ ์ด์ „ ํ˜•์ œ ํƒœ๊ทธ (previousSibling)

์ถ”๊ฐ€ ํ˜น์€ ์ œ๊ฑฐ๋œ ๋…ธ๋“œ์˜ ์ด์ „ ํ˜•์ œ DOM์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๋ณ€๊ฒฝ ์ด์ „์˜ ๊ฐ’ (oldValue)

๋ณ€๊ฒฝ ์ด๋ฒคํŠธ๊ฐ€ ๊ฐ์ง€๋œ ๊ฒฝ์šฐ, ๋ณ€๊ฒฝ ์ด์ „์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. attributeOldValue ๋ฐ characterDataOldValue ์˜ต์…˜์ด true์ผ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด๋ฒคํŠธ ๋Œ€์ƒ (target)

์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ํƒœ๊ทธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ด๋ฒคํŠธ ํƒ€์ž… (type)

๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์˜ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์•„๋ž˜์˜ ๊ฐ’ ์ค‘, ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์˜ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • attributes
  • characterData
  • childList

observer

MutationObserver ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ๋„ MutationObserver๋ฅผ ์—ฐ์‡„์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.

React์—์„œ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ

MutationObserver๋ฅผ ์ปค์Šคํ…€ ํ›…์„ ํ†ตํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด๋ณด์ž.

TYPESCRIPT

1
2
3
4
export function useMutationObserver(): void
{
  //
}

์œ„์™€ ๊ฐ™์ด useMutationObserver ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

useMutationObserver๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  ์•„๋ž˜์˜ ์„ธ ์š”์†Œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

  1. ๋Œ€์ƒ DOM
  2. ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
  3. ์˜ต์…˜

์œ„ ์„ธ ์š”์†Œ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ •์˜ํ•˜์ž. 1๋ฒˆ์˜ ๊ฒฝ์šฐ, ์ผ๋ฐ˜์ ์œผ๋กœ HTMLElement๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, ํ•ด๋‹น ํ›…์—์„œ๋Š” HTMLElement ํƒ€์ž… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, string์„ ๋ฐ›์•„ #id, .class์™€ ๊ฐ™์€ ์„ ํƒ์ž๋กœ ํƒœ๊ทธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { useEffect } from "react";

export type UseMutationObserverCallback = (entry: MutationRecord) => void;

/**
 * MutationObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseMutationObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {MutationObserverInit} options: ์˜ต์…˜
 */
export function useMutationObserver(ref: Element | string | null, callback: UseMutationObserverCallback, options: MutationObserverInit): void
{
	useEffect(() =>
	{
		const ro = new MutationObserver((entries) =>
		{
			entries.forEach(callback);
		});
	}, [ ref, callback, options ]);
}

MutationObserver๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ mo ๋ณ€์ˆ˜๋กœ ํ• ๋‹นํ•œ๋‹ค.

์ดํ›„ ref๋กœ ์ง€์ •ํ•œ DOM์„ ํ• ๋‹นํ•œ๋‹ค.

ref๋Š” ์„ธ ๊ฐ€์ง€ ์ƒํƒœ์— ๋”ฐ๋ผ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ถ„๊ธฐ๋ฅผ ๊ฑฐ์นœ๋‹ค.

  • null์ผ ๊ฒฝ์šฐ -> ํŒจ์Šคํ•œ๋‹ค.
  • string์ผ ๊ฒฝ์šฐ -> document.querySelector ๋ฉ”์„œ๋“œ๋กœ ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•œ ๋’ค, ํ•ด๋‹น ํƒœ๊ทธ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  • HTMLElement์ผ ๊ฒฝ์šฐ -> ์ด๋ฏธ Element์ด๋ฏ€๋กœ, ์ฆ‰์‹œ ๋“ฑ๋กํ•œ๋‹ค.

typeof ref === "string" ๊ตฌ๋ฌธ์„ ํ†ตํ•ด ref๊ฐ€ ๋ฌธ์ž์—ด์ธ์ง€๋ฅผ ํŒ๋ณ„ํ•˜์—ฌ ๊ตฌํ˜„ํ–ˆ๋‹ค.

mo.observe() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํƒœ๊ทธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { useEffect } from "react";

export type UseMutationObserverCallback = (entry: MutationRecord) => void;

/**
 * MutationObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseMutationObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {MutationObserverInit} options: ์˜ต์…˜
 */
export function useMutationObserver(ref: Element | string | null, callback: UseMutationObserverCallback, options: MutationObserverInit): void
{
	useEffect(() =>
	{
		const mo = new MutationObserver((entries) =>
		{
			entries.forEach(callback);
		});

		// DOM์ด ์œ ํšจํ•  ๊ฒฝ์šฐ
		if (ref)
		{
			// ref๊ฐ€ ๋ฌธ์ž์—ด์ผ ๊ฒฝ์šฐ
			if (typeof ref === 'string')
			{
				const tag = document.querySelector(ref);

				// ํƒœ๊ทธ๊ฐ€ ์œ ํšจํ•  ๊ฒฝ์šฐ
				if (tag)
				{
					mo.observe(tag, options);
				}
			}

			// DOM์ผ ๊ฒฝ์šฐ
			else
			{
				mo.observe(ref, options);
			}
		}
	}, [ ref, callback, options ]);
}

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ๋งˆ๋‹ค MutationObserver์˜ ๋“ฑ๋ก์ด ์ค‘์ฒฉ๋˜์ง€ ์•Š๋„๋ก, ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.

mo.disconnect() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด MutationObserver๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์ „์ฒด ์ฝ”๋“œ

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { useEffect } from "react";

export type UseMutationObserverCallback = (entry: MutationRecord) => void;

/**
 * MutationObserver ์ ์šฉ ํ›… ๋ฉ”์„œ๋“œ
 *
 * @param {Element | string | null} ref: Element
 * @param {UseMutationObserverCallback} callback: ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ
 * @param {MutationObserverInit} options: ์˜ต์…˜
 */
export function useMutationObserver(ref: Element | string | null, callback: UseMutationObserverCallback, options: MutationObserverInit): void
{
	useEffect(() =>
	{
		const mo = new MutationObserver((entries) =>
		{
			entries.forEach(callback);
		});

		// DOM์ด ์œ ํšจํ•  ๊ฒฝ์šฐ
		if (ref)
		{
			// ref๊ฐ€ ๋ฌธ์ž์—ด์ผ ๊ฒฝ์šฐ
			if (typeof ref === 'string')
			{
				const tag = document.querySelector(ref);

				// ํƒœ๊ทธ๊ฐ€ ์œ ํšจํ•  ๊ฒฝ์šฐ
				if (tag)
				{
					mo.observe(tag, options);
				}
			}

			// DOM์ผ ๊ฒฝ์šฐ
			else
			{
				mo.observe(ref, options);
			}
		}

		return () =>
		{
			mo.disconnect();
		};
	}, [ ref, callback, options ]);
}

์ „์ฒด ์ฝ”๋“œ๋Š” ์œ„์™€ ๊ฐ™๋‹ค.

CodeSandbox๋ฅผ ํ†ตํ•œ ์˜ˆ์‹œ

CodeSandbox๋กœ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์ด์™€ ๊ฐ™์ด MutationObserver๋ฅผ ํ™œ์šฉํ•˜๋ฉด DOM์˜ ๋ณ€ํ™”๋ฅผ ๋Šฅ๋™์ ์œผ๋กœ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ„ํ˜น, React ์ปดํฌ๋„ŒํŠธ์—์„œ ์š”์†Œ๋ฅผ ๋ณ€๊ฒฝํ•  ๊ฒฝ์šฐ, ์ด๋ฅผ ํƒ€ ์ปดํฌ๋„ŒํŠธ์—์„œ ์—ฐ๊ด€์ง€์–ด ์ถ”๊ฐ€์ ์ธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ํ•„์š”๊ฐ€ ์ƒ๊ธฐ๊ธฐ๋„ ํ•œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ปดํฌ๋„ŒํŠธ์— ๋ถˆํ•„์š”ํ•œ ํƒ€ ์ปดํฌ๋„ŒํŠธ ๋กœ์ง์„ ๋„ฃ๊ธฐ๋„ ํ•œ๋‹ค.

MutationObserver๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ด๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‚จ์€ ์˜ต์ €๋ฒ„๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ์‚ฌ์šฉ์„ฑ์ด ๋‚ฎ์€ ๊ฒƒ๋“ค์ด๋‹ค. ๊ทธ ์ค‘, ์„ฑ๋Šฅ๊ณผ ๊ด€๋ จ๋œ PerformanceObserver์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ด๋‹ค.


๐Ÿท๏ธ ํƒœ๊ทธ
# JavaScript
# TypeScript
# React
# Observer API
# MutationObserver

์ฝ์–ด์ฃผ์…”์„œ ๊ณ ๋งˆ์›Œ์š”!

๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด, ๊ณต๊ฐ์ด๋‚˜ ๋Œ“๊ธ€์„ ๋‹ฌ์•„์ฃผ์‹œ๋Š” ๊ฑด ์–ด๋–ค๊ฐ€์š”?

๋ธ”๋กœ๊ทธ ์šด์˜์— ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค.

https://hits.seeyoufarm.com/api/count/incr/badge.svg?count_bg=%23484848&icon=react.svg&icon_color=dodgerblue&title=view&title_bg=%23242424&url=https%3A%2F%2Fblog.itcode.dev%2Fposts%2F2024%2F06%2F24%2Fobserver-api-4