Jsdom: MutationObserver์— ๋Œ€ํ•œ ์ง€์› ์ถ”๊ฐ€

์— ๋งŒ๋“  2013๋…„ 06์›” 08์ผ  ยท  53์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: jsdom/jsdom

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ชจ๋“  53 ๋Œ“๊ธ€

์ด๊ฒƒ๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋‚˜๋ฅผ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ์•ˆ๋‚ดํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ธฐ๊บผ์ด ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@ SegFaultx64 ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ฒซ ๋ฒˆ์งธ ๊ฒŒ์‹œ๋ฌผ์—์„œ schettino72๊ฐ€ ์ œ๊ณตํ•œ URL์€ "์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ"์ž…๋‹ˆ๋‹ค.

๊ณตํ‰ํ•˜๊ฒŒ, ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด ํฐ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ๊ด€์ฐฐ์ž๋ฅผ ๋“ฑ๋กํ• ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋ฌผ๊ฑด์„ ๋ณด๊ด€ํ•˜๋Š” ๊ณณ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@ SegFaultx64 jsdom์˜ ํ•ด๋‹น ๋ถ€๋ถ„์„ ๋ณด์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ๋ฌด์—‡์ธ์ง€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. NS ๋ฉ”์†Œ๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ž‘์—…์„ ํ•  ๋•Œ(#727 ์ฐธ์กฐ; ๋ช…๋ชฉ์ƒ ๋ฒ„๊ทธ ์ˆ˜์ •์ด์ง€๋งŒ jsdom์˜ ๋‚ด์žฅ์— ์ƒ๋‹นํ•œ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค) ๋‚˜๋Š” ์œ ์‚ฌํ•œ ๊ตฌ์กฐ๋ฅผ ์ฐพ๊ณ  ์ƒˆ๋กœ์šด ํด๋ž˜์Šค/๋ฐ์ดํ„ฐ/ ๋“ฑ. ๋‚˜๋Š” ๋‚ด๊ฐ€ ๊ฐ€์žฅ ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๋Œ€๋กœ ์ง„ํ–‰ํ–ˆ๊ณ , Domenic์€ ๋ช‡ ๊ฐ€์ง€ ์˜๊ฒฌ์„ ๋งํ–ˆ๊ณ , ๋‚˜๋Š” ์กฐ์ •์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์กฐ์ •ํ–ˆ๊ณ  ๊ทธ๊ฒŒ ์ „๋ถ€์˜€์Šต๋‹ˆ๋‹ค.

@lddubeau ๋„ค, ํฌ์ธํ„ฐ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ MutationObserver https://github.com/megawac/MutationObserver.js/blob/master/MutationObserver.js์— ๋Œ€ํ•œ ๋ฉ‹์ง„ shim์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค

๋‚ด ์œ ์ผํ•œ ์กฐ์–ธ์€ https://dom.spec.whatwg.org/ ๋ฅผ ์ •๋…ํ•˜๊ณ  ๋Œ์—ฐ๋ณ€์ด ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋Œ€๊ธฐ์—ด์— ์žˆ๋Š” ์œ„์น˜์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์—ฌ์ „ํžˆ ์ƒˆ๋กœ์šด DOM ํ‘œ์ค€์œผ๋กœ ์ „ํ™˜ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— jsdom์—์„œ ์ ์ ˆํ•œ ๋Œ€์‘๋ฌผ์„ ์ฐพ๋Š” ๊ฒƒ์ด ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ์ง€๋งŒ ์ ์–ด๋„ ์‚ฌ์–‘์€ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์ง€์นจ์„ ์ œ๊ณตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@kresli ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!

์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ์ง€ ๋ชจ๋ฅด๊ฒ ์œผ๋‚˜ ๋ช‡๊ฐ€์ง€ ์‹คํ—˜์„ ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์•…์˜ ๊ฒฝ์šฐ๋Š” ๋‚ด๊ฐ€ ์กฐ๊ธˆ ๋ฐฐ์šฐ๊ณ , ์ตœ์„ ์˜ ๊ฒฝ์šฐ๋Š” ๋‚ด๊ฐ€ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ €๋Š” ํ˜„์žฌ jsdom์ด ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ณ  webidl์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€(๊ทธ๋ฆฌ๊ณ  jsdom์—์„œ ์‚ฌ์šฉ๋˜๋Š”์ง€) ์ดํ•ดํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

MutationObserver: https://dxr.mozilla.org/mozilla-central/source/dom/webidl/MutationObserver.webidl~~~

์ด์ œ ์กฐ๊ธˆ ๋” ์ž˜ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•  ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์—ฌ๊ธฐ์— ์„ค๋ช…๋œ ๊ฒƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. https://dom.spec.whatwg.org/#interface -mutationobserver - ๋งž์Šต๋‹ˆ๊นŒ?

@henrikkorsgaard ๋งž์Šต๋‹ˆ๋‹ค! jsdom์—์„œ ์ž‘๋™ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ๋ช‡ ๊ฐ€์ง€ ์ž‘์—…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ ์ ˆํ•œ MutationObserver.idl์„ https://github.com/tmpvar/jsdom/tree/master/lib/jsdom/living/nodes์— ์ถ”๊ฐ€
  • MutationObserver-impl.js ํŒŒ์ผ๋„ ํ•ด๋‹น ํด๋”์— ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค. ์—ฌ๊ธฐ์—์„œ ์‹ค์ œ ๊ตฌํ˜„ ์ž‘์—…์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์–‘์—๋Š” "๊ฐ MutationObserver ๊ฐœ์ฒด์—๋Š” ์ด๋Ÿฌํ•œ ๊ด€๋ จ ๊ฐœ๋…์ด ์žˆ์Šต๋‹ˆ๋‹ค."๋ผ๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ ์•„๋งˆ๋„ impl(= ๊ตฌํ˜„) ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ impl ํด๋ž˜์Šค์—์„œ ๊ด€์ฐฐ, ์—ฐ๊ฒฐ ํ•ด์ œ ๋ฐ takeRecords ๋ฉ”์„œ๋“œ์™€ ์ƒ์„ฑ์ž๋ฅผ "๊ทธ๋ƒฅ" ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ€๋Šฅํ•œ ํ•œ ์‚ฌ์–‘์„ ๋”ฐ๋ฅด๋„๋ก ๋…ธ๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ( @Sebmaster , ์ƒ์„ฑ์ž๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?)

์ด ๊ฒฝ์šฐ CharacterData์™€ ๊ฐ™์€ ๋‹จ์ˆœํ•œ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ๋งŽ์€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋‹ค๋ฅธ jsdom impl ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์–‘์—๋Š” "๊ฐ ๋…ธ๋“œ์—๋Š” ๋“ฑ๋ก๋œ ๊ด€์ฐฐ์ž์˜ ๊ด€๋ จ ๋ชฉ๋ก์ด ์žˆ์Šต๋‹ˆ๋‹ค."๋ผ๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” Node impl์— ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์–‘์€ ๋˜ํ•œ "๊ด€๋ จ๋œ ์œ ์‚ฌ ์ถœ์ฒ˜ ๋ธŒ๋ผ์šฐ์ง• ์ปจํ…์ŠคํŠธ์˜ ๊ฐ ๋‹จ์œ„์—๋Š” ์ดˆ๊ธฐ์— ์„ค์ •๋˜์ง€ ์•Š์€ ๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž ๋ณตํ•ฉ ๋งˆ์ดํฌ๋กœํƒœ์Šคํฌ ๋Œ€๊ธฐ์—ด ํ”Œ๋ž˜๊ทธ์™€ ์ดˆ๊ธฐ์— ๋น„์–ด ์žˆ๋Š” ๊ด€๋ จ MutationObserver ๊ฐœ์ฒด ๋ชฉ๋ก์ด ์žˆ์Šต๋‹ˆ๋‹ค."๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. jsdom์—์„œ "๊ด€๋ จ ์œ ์‚ฌ ์ถœ์ฒ˜ ๋ธŒ๋ผ์šฐ์ง• ์ปจํ…์ŠคํŠธ์˜ ๋‹จ์œ„"๊ฐ€ ๋ช…ํ™•ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์กฐ๊ธˆ ๋” ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋‚ด๊ฐ€ ํ•  ์ผ์€ ๊ทธ๊ฒƒ๋“ค์„ Window ๊ฐ์ฒด์— ๋†“๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Window ๊ฐ์ฒด๋Š” ์•„์ง IDL์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ Window.js์— ๋ฐ‘์ค„ ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™์€ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ฏธ๋ž˜์— ์šฐ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ ์ฐฝ์—์„œ ์‚ฌ๋ฌผ์ด ์ถ”์ ๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ฆ‰, iframe ๊ด€๋ฆฌ). ๊ทธ๋Ÿฌ๋‚˜ ํ˜„์žฌ๋กœ์„œ๋Š” ์ฐฝ์„ ๋‘๊ธฐ์— ์ข‹์€ ์žฅ์†Œ์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, "๋Œ์—ฐ๋ณ€์ด ๋ ˆ์ฝ”๋“œ๋ฅผ ํ์— ๋„ฃ๋Š”" ์ ์ ˆํ•œ ์œ„์น˜๋ฅผ ์ฐพ์•„์„œ ๊ตฌํ˜„์˜ ๋งŽ์€ ๋ถ€๋ถ„์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. https://dom.spec.whatwg.org/#queue -a-mutation-record๋กœ ์ด๋™ํ•˜์—ฌ "queue a mutation record"๋ฅผ ํด๋ฆญํ•˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ์‚ฌ์–‘์˜ ๋ชจ๋“  ์œ„์น˜๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. jsdom์—๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ๋•Œ ์‚ฌ์–‘์ด ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ๊ณผ ๋˜‘๊ฐ™์€ ํ›„ํฌ๊ฐ€ ๋ชจ๋‘ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์•ฝ๊ฐ„ ๊นŒ๋‹ค๋กœ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ jsdom์˜ _attrModified, _descendantRemoved ๋ฐ _descendantAdded ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. https://github.com/tmpvar/jsdom/blob/master/lib/jsdom/living/attributes.js ์—๋Š” "TODO ๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž ํ•ญ๋ชฉ"๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

์—„์ฒญ๋‚œ :)

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ์ €๋Š” webidl ๋ฐ ๋” ํฐ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ๋งŽ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋˜ํ•œ ๋ช‡ ๊ฐ€์ง€ ๊ธฐํ•œ์œผ๋กœ ์ธํ•ด ์•ฝ๊ฐ„ ๋ฌด๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค ;)

๋‚˜๋Š” ์ตœ์„ ์„ ๋‹คํ•˜๊ณ  ์•ž์œผ๋กœ ๋ช‡ ์ฃผ ๋™์•ˆ ๊ทธ๊ฒƒ์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ €๋„ ํ•œ๋ฒˆ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ™•์ธ,

๋‚˜๋Š” ์ง€๊ธˆ๊นŒ์ง€ idl๊ณผ Impl์„ ๋งŒ๋“ค๊ณ  ๊ทธ๊ฒƒ์„ window.js์— ์š”๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜๊ณ  ์ฐฝ ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ new MutationObserver()๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const window = jsdom("<html><body><div id='mutation'></div></body></html>").defaultView;

let observer = new window.MutationObserver(function(mutations){
      console.log(mutations);
});

์—ฌ๊ธฐ์— ์†์ž„์ˆ˜๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ๊ฑฐ๋‚˜ MutationObserver๋ฅผ ์ „์—ญ ๊ฐœ์ฒด๋กœ ๋…ธ์ถœํ•ด์•ผ ํ•˜๋Š” ๊ณณ์ด ์žˆ์Šต๋‹ˆ๊นŒ(์ฐฝ ๊ฐœ์ฒด์—์„œ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœ๋จ).

Window.js์— ๋‹ค์Œ ์ค„์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

const MutationObserver = require("../living/generated/MutationObserver");

์ด๋Ÿฐ ์งˆ๋ฌธ์„ ํ•ด์„œ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  jsdom ์•„ํ‚คํ…์ฒ˜/ํŒจํ„ด์„ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ๋„๋ก ๋ช‡ ๊ฐ€์ง€ ์ง„์ž…์ ์„ ์„ค์ •ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์•„ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค ๊ทธ ๋ถ€๋ถ„์„ ๊นœ๋นกํ–ˆ๋„ค์š”. https://github.com/tmpvar/jsdom/blob/28f00b30236d540df1777ca6c2c0ee9e5e19fe5b/lib/jsdom/living/index.js#L28 ๊ณผ ๊ฐ™์€ ์ค„์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋Œ์—ฐ๋ณ€์ด ์ž‘์—… ์„ @domenic ์˜ ์•„์ด๋””์–ด๊ฐ€ ์ž‘๋™ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค( webkit์€ ์ „์šฉ ์Šค๋ ˆ๋“œ/๋Œ€๊ธฐ์—ด์— ๋Œ์—ฐ๋ณ€์ด ๋ ˆ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค ). ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๋˜ํ•œ ๋Œ€๊ธฐ์—ด์˜ ์–ด๋–ค ํ˜•ํƒœ์™€ ๋” ์ค‘์š”ํ•˜๊ฒŒ๋Š” ๋Œ€๊ธฐ์—ด์— ์žˆ๋Š” ๊ฒƒ์„ ์‹คํ–‰ํ•˜๋Š” ์ผ๋ถ€ ๋…ผ๋ฆฌ๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‘ ๊ฐ€์ง€ ์งˆ๋ฌธ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋Œ€๊ธฐ์—ด์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” jsdom์˜ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์•„ํ‚คํ…์ฒ˜ ๋ฐ (๋ฏธ๋ž˜) ํ†ตํ•ฉ ์ธก๋ฉด์—์„œ ์—ฌ๊ธฐ์—์„œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

ํƒ€์ด๋จธ(jsdom์— ์ „์—ญ ํ‹ฑ์ด ์žˆ์Šต๋‹ˆ๊นŒ?) ๋˜๋Š” ๋‹จ์ง€ ์•ฝ์†/์™„๋ฃŒ ์ฝœ๋ฐฑ ๊ตฌ์กฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋Œ€๊ธฐ์—ด์—์„œ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํ˜„๋ช…ํ• ๊นŒ์š”?

์ €๋Š” ๋งค์šฐ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„์„ ์™„๋ฃŒํ•˜๊ณ  ๋ฏธ๋ž˜์˜ ๊ตฌํ˜„์„ ์•ˆ๋‚ดํ•˜๊ธฐ ์œ„ํ•ด ๊ด‘๋ฒ”์œ„ํ•œ ํ…Œ์ŠคํŠธ ์‚ฌ๋ก€๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋งˆ์ดํฌ๋กœํƒœ์Šคํฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ process.nextTick(fn) ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ณตํ•ฉ ๋งˆ์ดํฌ๋กœ ํƒœ์Šคํฌ ๋น„์ฆˆ๋‹ˆ์Šค์™€ "๋ณตํ•ฉ ๋งˆ์ดํฌ๋กœ ํƒœ์Šคํฌ ํ•˜์œ„ ํƒœ์Šคํฌ ์‹คํ–‰"์œผ๋กœ ์ธํ•ด ๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž์—๊ฒŒ ์กฐ๊ธˆ ๋” ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” _ ์ƒ๊ฐ _ ๋Œ€๋ถ€๋ถ„ ๊ทธ๊ฒƒ์„ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. jsdom์€ ์„ค์ • ์ค‘์ธ ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์‚ฌ์–‘์— "๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž์—๊ฒŒ ์•Œ๋ฆฌ๊ธฐ ์œ„ํ•ด ๋ณตํ•ฉ ๋งˆ์ดํฌ๋กœํƒœ์Šคํฌ ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€"๋ผ๊ณ  ํ‘œ์‹œ๋˜๋ฉด process.nextTick(notifyMutationObservers) ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ notifyMutationObservers ๋‚ด๋ถ€์—์„œ 3๋‹จ๊ณ„๋Š” ํ•˜์œ„ ๋‹จ๊ณ„๋ฅผ ๋™๊ธฐ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž์— ๋Œ€ํ•œ ๋ฃจํ”„์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด https://github.com/tmpvar/jsdom/blob/master/Contributing.md ๋ฅผ ํ™•์ธ

์ด๊ฒƒ์€ ์งˆ๋ฌธํ•˜๊ธฐ์— ์ ์ ˆํ•œ ์žฅ์†Œ๊ฐ€ ์•„๋‹ ์ˆ˜๋„ ์žˆ์ง€๋งŒ impl ํŒŒ์ผ(Node-impl -> MutationObserver-Impl) ๊ฐ„์— webidl ์Šคํ‚ค๋งˆ ๊ฐœ์ฒด(์˜ˆ: /generated/MutationRecord.js)๋ฅผ ์•ž๋’ค๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. .

MutationRecords์˜ ์Šคํ‚ค๋งˆ๋ฅผ ๋ฏธ๋Ÿฌ๋งํ•˜๋Š” ์ˆœ์ˆ˜ JSON ๊ฐœ์ฒด๋กœ ๊ฐœ์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๋Œ์—ฐ๋ณ€์ด๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ Node-impl์—์„œ MutationObserver๋กœ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ์œ ํ˜น์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์‚ฌ์–‘์— ์„ค๋ช…๋œ ๋Œ€๋กœ MutationObservers์˜ ๋ชจ๋“  ์ธก๋ฉด์„ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ์•ผ์‹ฌ์„ ๊นจ๋œจ๋ฆด ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด jsdom webidl ์•„ํ‚คํ…์ฒ˜/๊ฐ์ฒด ๋ชจ๋ธ/์ธํ„ฐํŽ˜์ด์Šค์— ์ต์ˆ™ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ ๋‚ด์˜ ์˜ˆ๋Š” ๋˜ํ•œ impl ํŒŒ์ผ์—์„œ /generated/ ๊ฐ์ฒด๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์•„๋งˆ๋„ ์ด๊ฒƒ์„ ์–ด๋”˜๊ฐ€์— ๋ฌธ์„œํ™”ํ•ด์•ผ ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์— ๊ฐ„๋‹ค:

์ผ๋ฐ˜์ ์œผ๋กœ ์ƒ์„ฑ๋œ API๋ฅผ ํ†ต๊ณผํ•˜๋Š” ๋ชจ๋“  ์ธ์ˆ˜๋Š” ์ž๋™์œผ๋กœ ์–ธ๋ฐ•์‹ฑ/๋ฆฌ๋ฐ•์‹ฑ๋˜๋ฏ€๋กœ ์ƒ์„ฑ๋œ ๊ฐ์ฒด์— ๋Œ€ํ•ด ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„ ๊ฐ์ฒด๋งŒ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ œ์™ธ - ์‹ค์ œ๋กœ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ด๋ฏธ IDL ๊ธฐ๋ฐ˜์œผ๋กœ _๋ชจ๋“  ๊ฒƒ_์„ ์ „ํ™˜ํ–ˆ๋‹ค๋ฉด ์ด๊ฒƒ์€ ์‚ฌ์‹ค์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ธ์ˆ˜ ๋ฐ ๋ฐ˜ํ™˜ ๊ฐ’์€ ์ž‘๋™ํ•˜์ง€๋งŒ ๋น„ IDL ํด๋ž˜์Šค(์˜ˆ: Window)์™€ ์ƒํ˜ธ ์ž‘์šฉํ•  ๋•Œ๋งˆ๋‹ค ์ธ์ˆ˜๋ฅผ ์ง์ ‘ ์ œ๊ณตํ•  ๋•Œ unboxing/reboxing์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ : https://github. com/tmpvar/jsdom/blob/master/lib/jsdom/living/events/EventTarget-impl.js#L103 , ์—ฌ๊ธฐ์„œ Window๋Š” ์•„์ง idl๋˜์ง€ ์•Š์•˜์ง€๋งŒ EventTarget(Windows๊ฐ€ ์ƒ์†ํ•˜๋Š” ๋ถ€ํ„ฐ) ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ idlUtils.wrapperForImpl / idlUtils.implForWrapper ์ˆ˜๋™ ๋ณต์‹ฑ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ผ๋ฐ˜์ ์œผ๋กœ Impl ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ์ƒ์„ฑ๋œ ํŒŒ์ผ์ด ํ•„์š”ํ•˜๊ณ  ๋‚ด๋ณด๋‚ด๊ธฐ์— ๋Œ€ํ•ด createImpl ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. args์˜ ๋ฐฐ์—ด(public-constructor ์ธ์ˆ˜์˜ ๊ฒฝ์šฐ)๊ณผ private args์˜ ๊ฐ์ฒด(๋‘˜ ๋ชจ๋‘ impl ํด๋ž˜์Šค์— ์ œ๊ณต๋จ)๋ฅผ ์ทจํ•ฉ๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์˜ˆ๋Š” https://github.com/tmpvar/jsdom/blob/9dd9069354e36c077032f4cbcb1616a7d9e6f0c4/lib/jsdom/living/nodes/Document-impl.js#L549 ๋ฅผ ์ฐธ์กฐ

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ „์ฒด๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํžˆ ๊นŠ์ด ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(์ œ ์ƒ๊ฐ์—๋Š”). ๋”ฐ๋ผ์„œ ๋” ๊ตฌ์ฒด์ ์ธ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ๊ธฐ๊บผ์ด ๋” ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@Sebmaster ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค, ๋งŽ์€ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ, API/๊ฐ์ฒด๋ฅผ ํ†ตํ•ฉํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์™€ ํ•จ๊ป˜ ์ด๊ฒƒ์„ ๋ฌธ์„œํ™”ํ•˜๋ฉด ๋„์›€์ด ๋  ๊ฒƒ์ด์ง€๋งŒ ๋‹ค์Œ ๋ช‡ ๋‹จ๊ณ„์— ๋Œ€ํ•ด์„œ๋Š” ์ถฉ๋ถ„ํžˆ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋น ๋ฅธ ์—…๋ฐ์ดํŠธ!
MutationRecord๊ฐ€ ์‚ฌ์–‘์„ ๋”ฐ๋ฅผ ๊ฒฝ์šฐ W3C ํ…Œ์ŠคํŠธ ๋Š” ์‹คํŒจํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค(์ œ ์ƒ๊ฐ์—๋Š”). ๋‚˜๋Š” ๊ทธ๋“ค์˜ ์ €์žฅ์†Œ์— ์˜๊ฒฌ์„ ๋‚จ๊ฒผ์Šต๋‹ˆ๋‹ค.

๋‚ด ๋ชฉ์ ์„ ์œ„ํ•ด ๋กœ์ปฌ๋กœ ์—…๋ฐ์ดํŠธํ•˜์ง€๋งŒ W3C/jsdom์— ์ปค๋ฐ‹ํ• ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋‚˜์—๊ฒŒ๋„ ๊ทธ ์ผ์„ ๋งˆ์น  ์‹œ๊ฐ„์ด ์—†๋‹ค๋ฉด ๋ง์ด๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์†์„ฑ ๋Œ์—ฐ๋ณ€์ด๋Š” ์ด์ œ ๋Œ€๋ถ€๋ถ„์˜ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ–ˆ์œผ๋ฉฐ ์ฃผ๋ง์ด๋‚˜ ๋‹ค์Œ ์ฃผ ์ดˆ์— pull ์š”์ฒญ์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•ฉ๋‹ˆ๋‹ค! ๋ฌธ์ œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” https://github.com/w3c/web-platform-tests/issues/2482 ๋ฅผ ๋”ฐ๋ผ๊ฐˆ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋งˆ๋„: MutationRecord์˜ ์†์„ฑ ๊ฐ’์ด ํ…Œ์ŠคํŠธ์—์„œ ์˜ˆ์ƒํ•˜๋Š” ๊ฒƒ๊ณผ ์‚ฌ์–‘์— ํ•„์š”ํ•œ ๊ฒƒ์ด ๋ฌด์—‡์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๊นŒ? ?

๋ฌธ์ œ๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ๋น„๊ตํ•  ์ „์ฒด mutationRecord๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฒซ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ ์‚ฌ๋ก€ ์—์„œ ํ…Œ์ŠคํŠธ๋Š” id ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋œ ๋Œ์—ฐ๋ณ€์ด ๋ ˆ์ฝ”๋“œ๋Š” ์ด์ „ ๊ฐ’ ์†์„ฑ์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. oldValue ์†์„ฑ์ด ์˜ˆ์ƒ ๊ฐœ์ฒด์— ์ •์˜๋˜์–ด ์žˆ์ง€ ์•Š์•„ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋Œ์—ฐ๋ณ€์ด ๋ ˆ์ฝ”๋“œ๋Š” null์ธ ๊ฒฝ์šฐ์—๋„ ํ•ญ์ƒ ๋ชจ๋“  ์†์„ฑ์„ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ DOMString null์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ์†์„ฑ์ด ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋Š” null(typeof ๊ฐœ์ฒด)๊ณผ null(typeof ๋ฌธ์ž์—ด)์„ ๋น„๊ตํ•˜๊ณ  ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ๋Š” ๋Š๋ฆฌ๊ฒŒ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ •์˜๋˜์ง€ ์•Š์€ ํ•„๋“œ๋Š” ์ž๋™์œผ๋กœ null(๊ฐ์ฒด)๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ a) mutationRecord๊ฐ€ ์˜ˆ์ƒ ๋ ˆ์ฝ”๋“œ ๊ฐ์ฒด(์˜ˆ: oldValue)์— ์„ค์ •๋˜์ง€ ์•Š์€ ์†์„ฑ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  b) mutation ๋ ˆ์ฝ”๋“œ ์†์„ฑ์ด DOMstring null(์˜ˆ: attributeNamespace)์ผ ๋•Œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๋งž๋Š” ๋ง์ด๋‹ค?

๋‚ด๊ฐ€ ์˜ณ๋‹ค๋ฉด ๊ณ ์น  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ํ•˜๋‚˜์”ฉ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ jsdom ๋ฐ w3c ํ…Œ์ŠคํŠธ ๋ชจ๋‘์— ๋Œ€ํ•œ ์™ธ๋ถ€์ธ์œผ๋กœ์„œ ์•ฝ๊ฐ„ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๋‹ค์Œ ํ˜•์‹์˜ ๋‹ต๋ณ€์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ์—์„œ๋Š” oldValue๊ฐ€ null์ด๊ฑฐ๋‚˜ ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์ง€๋งŒ ์‚ฌ์–‘์€ "null" ๊ฐ’์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์œ„์—์„œ ์ฐธ์กฐํ•œ ํ…Œ์ŠคํŠธ๋Š” ์•”์‹œ์ ์œผ๋กœ oldValue๊ฐ€ null์ผ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค. "n"์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น ํŒŒ์ผ์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์‚ฌ๋ก€๋Š” attributeNamespace๊ฐ€ null(๊ฐ์ฒด)์ผ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๋ฉฐ ์‚ฌ์–‘์— ๋”ฐ๋ผ DOMString null์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

attributeNamespace์˜ ์œ ํ˜•์€ DOMString?์ด๋ฏ€๋กœ null์ด ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค("null"์ด ์•„๋‹ˆ๋ผ null๋งŒ).

oldValue ์‚ฌ๋ก€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. :).

๋„ค, ๋ช…ํ™•ํžˆ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์–‘์˜ ์ค‘์š”ํ•œ ์ธก๋ฉด์„ ๋†“์นœ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‚ด ํฌํฌ์— MutationObserver ๋ถ„๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ํ‘ธ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ Attribute ๋ฐ CharacterData ๋Œ์—ฐ๋ณ€์ด๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ w3c ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์•Œ๋ ค์ง„ ์ด์œ /๋ฌธ์ œ(๋ฒ”์œ„ ์ง€์› #317 ๋ˆ„๋ฝ ๋“ฑ)๋กœ ์ธํ•ด ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•œ ํ…Œ์ŠคํŠธ๋Š” ์–ด๋””์— ๋ฌธ์„œํ™”ํ•ฉ๋‹ˆ๊นŒ?

์šฐ๋ฆฌ๊ฐ€ ํ•˜๋Š” ์ผ์€ ์›น ํ”Œ๋žซํผ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด index.js ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ ์ด์œ ์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ํ•จ๊ป˜ ์ฃผ์„ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์šฐ๋ฆฌ๊ฐ€ ํ…œํ”Œ๋ฆฟ์— ๋Œ€ํ•ด ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด๊ฒŒ ์™„๊ฒฐ๊นŒ์ง€ ๋ฉ€์—ˆ๋‚˜์š”?

๋Œ์—ฐ๋ณ€์ด ์ด๋ฒคํŠธ๊ฐ€ 8.5์—์„œ ์ œ๊ฑฐ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค . 8.5๋กœ ๋Œ์•„๊ฐ€์„œ ํด๋ฆฌํ•„์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ(๋Œ์—ฐ๋ณ€์ด ๊ด€์ฐฐ์ž ํ•„์š”)๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? DOMParser ๋Š” 8.5์—์„œ ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ Shadow DOM ํด๋ฆฌํ•„์„ ์œ„ํ•ด ์ด๋ฅผ ์š”๊ตฌํ•˜๋ฏ€๋กœ ํ˜„์žฌ JSDOM์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์–ด๋–ค ์ง€์นจ์ด ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„ ์ง€๊ธˆ ๋‹น์žฅ์€ ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋…ธ๋ ฅํ•˜๊ณ  ๋„์›€์„ ์ค„ ๋Šฅ๋ ฅ์ด ์—†์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๋ ‡๊ฒŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค :(.

์ด ๋ฌธ์ œ๋ฅผ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”? https://github.com/henrikkorsgaard/jsdom/commits/MutationObserver ์—์„œ ์ผ๋ถ€ ์ž‘์—…์ด ์‹œ์ž‘๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์„ ์˜ฎ๊ธฐ๊ธฐ ์œ„ํ•ด ์—ฌ์ „ํžˆ ๋ฌด์—‡์ด ํ•„์š”ํ•œ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. MutationObserver ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๊ณณ์—์„œ ์ง€์›๋œ๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ์ด๊ฒƒ์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์€ ํฐ ๊ฒฉ์ฐจ์ฒ˜๋Ÿผ ๋Š๊ปด์ง‘๋‹ˆ๋‹ค. http://caniuse.com/#feat =mutationobserver

webcomponentsjs ํด๋ฆฌํ•„์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? https://github.com/webcomponents/webcomponentsjs

ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋Š” ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค. @henrikkorsgaard ์˜ ์ž‘์—…์€ ํ™•์‹คํžˆ ์‹œ์ž‘ํ•˜๊ธฐ์— ์ข‹์€ ์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํด๋ฆฌํ•„์ด ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ”์— ๋”ฐ๋ฅด๋ฉด ๋Œ์—ฐ๋ณ€์ด ์ด๋ฒคํŠธ๋Š” ์„ฑ๋Šฅ ๊ณ ๋ ค ์‚ฌํ•ญ ๋•Œ๋ฌธ์— ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Mutation Observer๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์ง€์ •๋˜๊ณ  ๊ตฌํ˜„๋œ ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ตœ์†Œํ•œ MO ์ง€์›์ด ๊ตฌํ˜„๋  ๋•Œ๊นŒ์ง€ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์œ ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์‹ค์šฉ์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ด์— ๋Œ€ํ•œ ์ง€์›์„ ๋‹ค์‹œ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ตœ์†Œํ•œ MO๋Š” ์ด๊ฒƒ์ด ๊ตฌํ˜„๋  ๋•Œ๊นŒ์ง€ ํด๋ฆฌํ•„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JSDOM์˜ ์œ ์ง€ ๊ด€๋ฆฌ์— ์ด์ƒ์ ์ด์ง€ ์•Š์œผ๋ฉฐ ๊ทธ ์ดํ›„๋กœ ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ๋ถ„๊ธฐ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์Œ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๊ณ ๋ คํ•ด ๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํŠนํžˆ ์›น ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํ”„๋กœ๋•์…˜ ์•ฑ์„ ์œ„ํ•œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์†”๋ฃจ์…˜์ด๊ณ  MO๋ฅผ ํด๋ฆฌํ•„ํ•ด์•ผ ํ•˜๋Š” ์ด ๋‹จ๊ณ„์—์„œ ํฐ ๊ฒฉ์ฐจ๋ฅผ ๋‚จ๊น๋‹ˆ๋‹ค. JSDOM์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋„์›€์ด๋œ๋‹ค๋ฉด; ๋‚˜๋Š” ํด๋ฆฌ๋กœ ์ฑ„์›Œ์ง„ MO on-top jsdom; ์—ฌ๊ธฐ ๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ . ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ์ข‹์€ ํ•ด๊ฒฐ์ฑ…์ด๋ผ๊ณ  ๋งํ•˜์ง€๋Š” ์•Š๊ฒ ์ง€๋งŒ ํƒ€์ด๋จธ ์—†์ด๋„

4๋…„ ์ „์ธ๋ฐ ์•„์ง๋„ ์ง„ํ–‰์ค‘์ธ๊ฐ€์š”? ์ด ๋ฌธ์ œ๋Š” ๋ฒ„์ „ 9.0.0 ์ดํ›„ ๋ณ€๊ฒฝ ๋กœ๊ทธ์— ์–ธ๊ธ‰๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„, ์ด๊ฒƒ์— ๋Œ€ํ•œ ์†Œ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@MeirionHughes ํŒŒ์ผ์ด ์›ํ™œํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@domenic ์˜ ๋งํฌ์— ๋Œ€ํ•œ ํ™•์žฅ์ž…๋‹ˆ๋‹ค.
@MeirionHughes๊ฐ€ ์ œ์•ˆํ•œ ํ•ด๋‹น ํŒŒ์ผ์„ ๋„ฃ์œผ๋ ค๊ณ  ํ•˜๋ฉด ์ง€๊ธˆ๊นŒ์ง€ ์ž˜ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

@mtrabelsi @MeirionHughes ์•ˆ๋…•ํ•˜์„ธ์š”, Jest์—์„œ ์ž‘๋™ํ•˜๋„๋ก ๊ด€๋ฆฌํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. https://stackoverflow.com/questions/43190171/jsdom-cannot-read-property-location-of-null
JSDOM์—์„œ MO๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋Š”์ง€ ์„ค๋ช…ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

ํŽธ์ง‘ํ•˜๋‹ค:
์ข‹์•„, ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ž‘๋™ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ ๊ฐ™๋‹ค https://gist.github.com/romuleald/1b9272fce11d344e257d0bdfd3a984b0

@romuleald ์š”์ ์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. this.expando ๋ฐ this.counter ๋ฅผ ์„ค์ •ํ•œ ๋‹ค์Œ ์ •์ ์œผ๋กœ ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ _findMutations ์—์„œ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ณ€ํ™˜ํ•˜๋ ค๋Š” typescript ํŒŒ์ผ์—์„œ ๋‘ ์†์„ฑ์ด ๋ชจ๋‘ ์ •์ ์ž„์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ES6์—์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅ). Util ํด๋ž˜์Šค ์•„๋ž˜์— ๋‹ค์Œ ์ค„์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Util.counter = 1;
Util.expando = 'mo_id';

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒ์„ฑ์ž๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํด๋ž˜์Šค๋Š” ์–ด์จŒ๋“  ์ธ์Šคํ„ด์Šคํ™”๋˜์ง€ ์•Š์Œ).

์ง€๋‚œ ํ•˜๋ฃจ ๋ฐ˜ ๋™์•ˆ ์ด ๋ฌธ์ œ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐ ์ฐฝํ”ผํ•  ์ •๋„๋กœ ์˜ค๋žœ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ shim์˜ ๊ธฐ๋ฐ˜์ด ๋œ ์†Œ์Šค๋Š” CharacterData ๋…ธ๋“œ์˜ .data ์†์„ฑ ๋ณ€๊ฒฝ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์ˆ˜์ •์„ ์œ„ํ•ด ์ด ๋Œ“๊ธ€ ์œ„์— ๋‚ด ์—ฐ๊ฒฐ๋œ ๋ฌธ์ œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹น๋ถ„๊ฐ„ https://github.com/megawac/MutationObserver.js ํด๋ฆฌํ•„์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” AVA๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  '''๋Š” MutationObserver๋ฅผ ํฌํ•จํ•˜๋Š” ๋‚ด ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

import test from 'ava';
import delay from 'delay';
import jsdom from 'jsdom';
import m from '.';

const dom = new jsdom.JSDOM();
global.window = dom.window;
global.document = dom.window.document;

require('mutationobserver-shim');

global.MutationObserver = window.MutationObserver;

test('MutationObserver test', async t => {
    delay(500).then(() => {
        const el = document.createElement('div');
        el.id = 'late';
        document.body.appendChild(el);
    });

    const checkEl = await m('#late');
    t.is(checkEl.id, 'late');
});

Polyfill์ด๊ธฐ ๋•Œ๋ฌธ์— ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋‹ค๋ฅธ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฌธ์ œ๋Š” ์ง„์ „์ด ์—†๋Š” ๊ฒƒ ๊ฐ™์œผ๋‹ˆ ํ•˜๋‚˜์˜ ์˜ต์…˜์œผ๋กœ ์ฐธ๊ณ ํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

jsdom ๋ฐ jest๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ฆฌํ•„๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ shim์„ ์Šคํฌ๋ฆฝํŠธ๋กœ ์ˆ˜๋™์œผ๋กœ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

npm install mutationobserver-shim

์˜ˆ๋ฅผ ๋“ค์–ด beforeAll ํ•จ์ˆ˜ ๋‚ด๋ถ€:

const mo = fs.readFileSync(
  path.resolve('node_modules', 'mutationobserver-shim', 'dist', 'mutationobserver.min.js'),
  { encoding: 'utf-8' },
);
const moScript = win.document.createElement('script');
moScript.textContent = mo;

win.document.body.appendChild(moScript);

์ด "ํ•ดํ‚น"์€ ๋‚˜๋ฅผ ์œ„ํ•ด, ์•„๋งˆ๋„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ๋„ ํšจ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค ;)

IIRC Jest์šฉ setupFiles์˜ ์ผ๋ถ€๋กœ shim(pal-nodejs์—์„œ ์กฐ์ •๋œ ๋ฒ„์ „์˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์œ„์˜ npm ํŒจํ‚ค์ง€๊ฐ€ ๋ฌด์—‡์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Œ)์„ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ์œผ๋ฉด ๋‹ค๋ฅธ ๋žฉํ†ฑ์—์„œ ๋” ๊ตฌ์ฒด์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜คํ”ˆํ•œ์ง€ ๊ฑฐ์˜ 5๋…„ ์ •๋„ ๋˜์—ˆ๋Š”๋ฐ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ๋‚˜์š”?

@mendrik ์กฐ๋กฑ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ–ˆ์Šต๋‹ˆ๋‹ค https://github.com/benitogf/corsarial/blob/master/test/specs/utils.js#L29 ์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ํด๋ฆฌํ•„ ์†”๋ฃจ์…˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ํ•˜๋ฃจ ๋˜์„ธ์š” :)

https://github.com/skatejs/skatejs/blob/master/packages/ssr/register/MutationObserver.js์— ๋ถ€๋ถ„ ๊ตฌํ˜„์ด ์žˆ์Šต๋‹ˆ๋‹ค

@benitogf ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์–ด๋–ป๊ฒŒ ๋“  ๋ ˆ์ฝ”๋“œ๊ฐ€ ๋‚˜๋ฅผ ์œ„ํ•ด ๋ฐœ์‚ฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค :(์˜ค๋ฅ˜๋ฅผ ๋˜์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
@treshugart ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๊นŒ? :)

ํ…Œ์ŠคํŠธ์—์„œ @mendrik , https://github.com/aurelia/pal-nodejs/blob/master/src/polyfills/mutation-observer.ts ๊ฐ€์ ธ์˜ค๊ธฐ
์ „์—ญ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฑฐ์•ผ!

@mendrik JSDOM https://github.com/skatejs/skatejs/tree/master/packages/ssr#usage ๋ฅผ ์˜ตํŠธ์•„์›ƒํ•˜๋ ค๋Š” ๊ฒฝ์šฐ

์ด๊ฒƒ์€ react-quill์„ ํ™•์žฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

import 'mutationobserver-shim';

document.getSelection = () => null;
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰