dev/javascript

web component API ์‚ฌ์šฉ๊ธฐ (1) _๊ทธ๊ฒŒ ๋ญ”๋ฐ?

๋นŒ๋ผ๋„ฌ 2022. 7. 17. 17:45

์•„์ฃผ ๋ฐ”์œ ๋‚˜๋‚ ์„ ๋ณด๋‚ด๊ณ  ์žˆ๋‹ค. 

์•„์ฃผ์•„์ฃผ ๋ฐ”์˜๊ณ  ๋นก์„ธ์ง€๋งŒ ๋˜ ๊ทธ๋งŒํผ ์—„์ฒญ๋‚œ ์„ฑ์ทจ๊ฐ๊ณผ ์žฌ๋ฏธ๋กœ ๊ฐ€๋“์ฐฌ ํ•˜๋ฃจ๋“ค์ด๋‹ค! 

์šฐ์•„ํ•œํ…Œํฌ์บ ํ”„ 2์ฃผ์ฐจ ๋ฏธ์…˜์€ vanilla js ์œผ๋กœ 'ํˆฌ๋‘ ๋ฆฌ์ŠคํŠธ ๊ตฌํ˜„ํ•˜๊ธฐ' ์˜€๋‹ค.

( ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€๋ณ๊ฒŒ ์ƒ๊ฐํ•˜๋Š” ๊ทธ ํˆฌ๋‘ ๋ฆฌ์ŠคํŠธ(?)๊ฐ€ ์•„๋‹Œ ํŠธ๋ ๋กœ, ์ง€๋ผ ๋“ฑ ํ˜‘์—… ๋„๊ตฌ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์นธ๋ฐ˜ ๋ณด๋“œ ํ˜•ํƒœ์˜ ํˆฌ๋‘ ๋ฆฌ์ŠคํŠธ ์˜€๋‹ค ใ…Žใ…Ž..๐Ÿ™„ )  

 

์šฐ๋ฆฌ ํŒ€์€ ํ”„๋กœ์ ํŠธ์— web component๋ฅผ ๋„์ž…ํ•˜์˜€๊ณ , ์ด ๊ฒฝํ—˜์€ ์šฐ๋ฆฌ์—๊ฒŒ ์ƒˆ๋กœ์šด ๋„์ „์ด์˜€๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ๊ณผ์ •์—์„œ ๊ฒช์—ˆ๋˜ ๋ฌธ์ œ๋“ค๊ณผ ๋ฐฐ์šด์ ๋“ค์„ ๊ธฐ๋กํ•˜๊ณ ์ž ํ•œ๋‹ค. 

 

web component ๋ž€? 

์›น ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์ฝ”๋“œ์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์—์„œ ์บก์Аํ™”๋œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ์š”์†Œ๋ฅผ ๋งŒ๋“ค๊ณ  ์›น ์•ฑ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ  ๋ชจ์Œ  - mdn

mdn ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด web component์— ๋Œ€ํ•ด ์œ„์™€ ๊ฐ™์ด ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ vanilla js ๊ธฐ๋ฐ˜์˜ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋‹ค. 

 

์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ •์˜ํ•˜๊ณ , ์ •์˜ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŽ˜์ด์ง€ ๋‚ด์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

react์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•œ ๋ถ„๋“ค์—๊ฒŒ๋Š” ์–ด๋”˜๊ฐ€ ์ต์ˆ™ํ•œ ๊ทธ๋ฆผ์ด ๊ทธ๋ ค์งˆ ๊ฒƒ์ด๋‹ค. ์ด์ „์˜ ์ „ํ†ต์ ์ธ ๋งˆํฌ์—… ๋ฐฉ์‹์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” UI ์š”์†Œ๋“ค์ด ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ํž˜๋“  ๊ตฌ์กฐ๋ผ ๋ณต์žกํ•˜๊ณ  ๋น„ํšจ์œจ์ ์ด์˜€๋Š”๋ฐ web component๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด react์—์„œ ์‚ฌ์šฉํ•˜๋˜ ๋ฐฉ์‹ ์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค! 

 

web component ๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ช‡๊ฐ€์ง€ ๊ธฐ์ˆ ์  ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. 

  •  custom element
  •  shadowDOM
  •  HTML <template>, <slot>

๋จผ์ € custom element ๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž๐Ÿ”ฅ

custom element 

ํŽ˜์ด์ง€์— ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” API ์ด๋‹ค. 

coustomElementRegistry.define('๋ถ€์—ฌํ•  ์š”์†Œ ์ด๋ฆ„', '์š”์†Œ์˜ ๋™์ž‘์„ ์ •์˜ํ•˜๋Š” class ๊ฐ์ฒด', '์†์„ฑ์ด ํฌํ•จ๋œ ์˜ต์…˜')

coustomElementRegistry.define() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์†Œ๋ฅผ ์ •์˜ํ•œ๋‹ค. 

์ด๋•Œ ๋ถ€์—ฌํ•  ์š”์†Œ์˜ ์ด๋ฆ„์€ kebab-case๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‘ ๋‹จ์–ด๋ฅผ ์กฐํ•ฉํ•œ ์ด๋ฆ„์ด์—ฌ์•ผํ•œ๋‹ค! ( HTML ๊ธฐ๋ณธ ํƒœ๊ทธ์™€์˜ ์ถฉ๋Œ ๋ฐฉ์ง€ ) 

//์š”์†Œ์˜ ์ž‘๋™๋ฐฉ์‹ ๋ฐ UI ํ‹€์„ ๋งŒ๋“œ๋Š” ํด๋ž˜์Šค ์ •์˜
class CustomButton extends HTMLElement{
	constructor(){
    	super()
    }
    //shadowDOM ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด mode 'open'ํ•ด์คŒ
    this.attachShadow({mode:'open'})
    
    const wapper = document.createElement('div')
    wapper.setAttribute('class', 'wapper')
    
    const icon = wapper.appendChild(document.creatElement('span')
    
    //shadowRoot์— wrapper ์š”์†Œ๋ฅผ ๋„ฃ์–ด์คŒ
    this.shadowRoot.append(wrapper)
}

//๋‚˜๋Š” CustomButton ๊ธฐ๋ฐ˜์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ icon-button ์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ •์˜ํ• ๊ป˜~ 
customElements.define('icon-button', CustomButton);

//๋‚ด๊ฐ€ ์ •์˜ํ•œ ์š”์†Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค. 
<icon-button> </icon-button>

๋Œ€๋žต ์œ„์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

๊ทธ๋Ÿผ ์ด์ œ ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ์˜ class ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜๋ฉฐ ์‚ฌ์šฉํ•œ shadowDOM ์ด๋ž€ ๋ฌด์—‡์ธ์ง€ ๊ถ๊ธˆํ•  ๊ฒƒ์ด๋‹ค. 

 

shadowDOM

shadowDOM ์€ ๋‚ด๊ฐ€ ์ •์˜ํ•œ ์š”์†Œ๋ฅผ ์บก์Аํ™” ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐœ๋…์ด๋‹ค. ์š”์†Œ์˜ style, html ๋“ค์˜ ๊ธฐ๋Šฅ์„ ๋น„๊ณต๊ฐœ๋กœ ์œ ์ง€ํ•จ์œผ๋กœ์จ ๋ฌธ์„œ ์•ˆ์˜ ๋‹ค๋ฅธ ์š”์†Œ๋“ค๊ณผ ์ถฉ๋Œํ•  ์—ผ๋ ค ์—†์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์Šคํƒ€์ผ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. 

์ฒ˜์Œ์—๋Š” ์ด ์„ค๋ช…์ด ์ดํ•ด๊ฐ€ ์ž˜ ์•ˆ๋  ์ˆ˜๋„ ์žˆ๋‹ค. ๋‚ด๊ฐ€ ํ•œ๋ฒˆ์— ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋˜ ์˜ˆ์‹œ๊ฐ€ ์žˆ๋‹ค. 

<input type='range'/>

input ํƒœ๊ทธ์— type์„ 'range'๋กœ ์„ค์ •ํ•˜๋ฉด ์–ด๋–ค ์š”์†Œ๊ฐ€ ์ถœ๋ ฅ๋ ๊นŒ? 

range input ์˜ ๊ธฐ๋ณธ UI๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. 

<input type="range"/>

์ด์ œ ๊ฐœ๋ฐœ์ž๋„๊ตฌ ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์˜ ํ†ฑ๋‹ˆ๋ฐ”ํ€ด ์„ค์ • ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์„ค์ •์„ ๋ฐ”๊ฟ”๋ณด์ž. 

preferences ํƒญ์˜ Elements ๋ถ€๋ถ„์„ ๋ณด๋ฉด 'show user agent shadow DOM' ์ด ์žˆ๋‹ค. ์ด ์˜ต์…˜์„ ์ฒดํฌํ•ด์ฃผ๋ฉด ๊ฐœ๋ฐœ์ž๋„๊ตฌ์—์„œ ์ˆจ๊ฒจ์ ธ ์žˆ๋˜ shadow DOM ๋‚ด๋ถ€ ์š”์†Œ๋“ค์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

#shadow-root ๊ฐ€ ๋ณด์ด๊ณ  ํด๋ฆญํ•˜๋ฉด ๊ทธ๋™์•ˆ ์ˆจ๊ฒจ์ ธ ์žˆ๋˜ ์š”์†Œ๋“ค์ด ๋ณด์ธ๋‹ค. 

range type์˜ input ์š”์†Œ๋Š” 3๊ฐœ์˜ div ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

์ด๋ ‡๊ฒŒ shadowDOM ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๋ถ€ ์š”์†Œ์˜ className ์œผ๋กœ 'thumb'๋ฅผ ์‚ฌ์šฉํ–ˆ์–ด๋„ ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•œ 'thumb'๊ณผ ์ถฉ๋Œํ•˜์ง€ ์•Š๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 

์ถœ์ฒ˜:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM

shadowDOM mdn ๋ฌธ์„œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” shadowDOM ๊ตฌ์กฐ์ด๋‹ค. ์ผ๋ฐ˜ DOM์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” shadow host ๋‚ด๋ถ€์— shadow tree๊ฐ€ ์žˆ๊ณ , shadow tree์˜ ๋ฃจํŠธ ๋…ธ๋“œ๋ฅผ shadow root๋ผ๊ณ  ํ•œ๋‹ค. 

 

์•„๊นŒ ์˜ˆ์‹œ์—์„œ ๋ดค๋˜ <icon-button> ์€ shadow host, CustomButton์˜ ๊ฐ€์žฅ ์ตœ์ƒ๋‹จ ์š”์†Œ์ธ wrapper div ๋ฅผ shadow root๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค! 

 

attachShadow({mode:'open'}) ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ •์˜ ์š”์†Œ๋ฅผ shadow Root์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

shadow.appendChild(์š”์†Œ)  ๋˜๋Š” shadow.append(์š”์†Œ1, ์š”์†Œ2, ...) ์ƒ์„ฑํ•œ ๋ชจ๋“  ์š”์†Œ๋ฅผ shadow Root์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” web component๋ฅผ <template>, <slot> ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์‹ค์ œ๋กœ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ–ˆ๋˜ custom component ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ•ด๋ณด๊ฒ ๋‹ค. ๐Ÿ’ซ

'dev > javascript' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[ JS ] ๊ฐ์ฒด ์กฐํšŒ ๋ฐฉ๋ฒ• Dot notation vs Square-Bracket notation  (0) 2022.04.22