Shadow DOM ํ†บ์•„๋ณด๊ธฐ ๐Ÿ”Ž

์šฐํ˜ยท2024๋…„ 8์›” 5์ผ
8

FE

๋ชฉ๋ก ๋ณด๊ธฐ
6/11
post-thumbnail

Shadow DOM์ด๋ž€?

์›น ์ปดํฌ๋„ŒํŠธ์˜ ํ•ต์‹ฌ ๊ธฐ์ˆ  ์ค‘ ํ•˜๋‚˜๋กœ, HTML ๋ฌธ์„œ ๋‚ด์—์„œ ๋…๋ฆฝ์ ์ธ DOM ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ์Šคํƒ€์ผ๊ณผ ์ž๋ฐ”์ŠคํŠธ๋ฆฝํŠธ๊ฐ€ ์„œ๋กœ ์ถฉ๋Œํ•˜์ง€ ์•Š๋„๋ก ๊ฒฉ๋ฆฌ๋œ ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.


Shadow DOM์˜ ์ฃผ์š” ํŠน์ง•

1. ์บก์Аํ™”: Shadow DOM์€ ์™ธ๋ถ€ ์Šคํƒ€์ผ๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ๋…๋ฆฝ์ ์ธ DOM ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
์ด๊ฒƒ์€ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋‚˜ ๋ฌธ์„œ์˜ CSS์™€ JavaScript์™€ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

2. ๊ตฌ์กฐํ™”: ๋ณต์žกํ•œ UI ์š”์†Œ๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์ฒด์ ์ธ DOM์„ ๊ฐ€์ง€๋ฏ€๋กœ, ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋œ๋‹ค.

3. ์Šคํƒ€์ผ ๊ฒฉ๋ฆฌ: Shadow DOM ๋‚ด์˜ ์Šคํƒ€์ผ์€ ํ•ญ์ƒ Shadow DOM์—๋งŒ ์ ์šฉ๋˜๋ฉฐ, ์™ธ๋ถ€ ์Šคํƒ€์ผ์€ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ, ์Šคํƒ€์ผ์˜ ์ถฉ๋Œ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

4. ์žฌ์‚ฌ์šฉ์„ฑ: Shadow DOM์„ ์ด์šฉํ•˜๋ฉด UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์–‘ํ•œ ํ”„๋กœ์ ํŠธ์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


Shadow DOM์˜ ๊ตฌ์กฐ

Shadow DOM์€ ์ˆจ๊ฒจ์ง„ DOM ํŠธ๋ฆฌ๊ฐ€ ํ†ต์ƒ์ ์ธ DOM ํŠธ๋ฆฌ์— ์†ํ•œ ์š”์†Œ์— ๋ถ€์ฐฉ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

์ด Shadow DOM ํŠธ๋ฆฌ๋Š” shadow root์—์„œ ์‹œ์ž‘๋˜๋ฉฐ, ์›ํ•˜๋Š” ๋ชจ๋“  ์š”์†Œ ์•ˆ์— ๋ถ€์ฐฉ๋  ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ณผ์ •์€ ์ผ๋ฐ˜ DOM์—์„œ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•˜๋‹ค.

๐Ÿ’ก Shadow DOM ์šฉ์–ด

  • Shadow host: Shadow DOM์ด ๋ถ€์ฐฉ๋˜๋Š” ํ†ต์ƒ์ ์ธ DOM ๋…ธ๋“œ
  • Shadow tree: Shadow DOM ๋‚ด๋ถ€์˜ DOM ํŠธ๋ฆฌ
  • Shadow boundary: Shadow DOM์ด ๋๋‚˜๊ณ , ํ†ต์ƒ์ ์ธ DOM์ด ์‹œ์ž‘๋˜๋Š” ์žฅ์†Œ
  • Shadow root: Shadow ํŠธ๋ฆฌ์˜ root ๋…ธ๋“œ

Shadow DOM์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

Element.attachShadow() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ด๋– ํ•œ ์š”์†Œ์—๋“  Shadow root๋ฅผ ๋ถ€์ฐฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๋ฉ”์†Œ๋“œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ•˜๋‚˜์˜ ๋ชจ๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ชจ๋“œ ๊ฐ์ฒด๋ฅผ ํ•„์š”๋กœ ํ•œ๋‹ค.

์ด ๋ฉ”์†Œ๋“œ๋Š” HTML ์š”์†Œ์— ๋Œ€ํ•ด Shadow root๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

๐Ÿšจ ์ฃผ์˜ ์‚ฌํ•ญ
์ฃผ๋กœ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ํŠน์ • ์š”์†Œ๋“ค์€ Shadow host๊ฐ€ ๋  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ˆœํžˆ <a> ์š”์†Œ๋ฅผ Shadow host๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค!

// HTML ์š”์†Œ ์„ ํƒ
const hostElement = document.querySelector('.element');

// Shadow DOM ์ƒ์„ฑ
const shadowRoot = hostElement.attachShadow({ mode: 'open' }); // or ({ mode: "closed" })

// Shadow DOM์— ์ฝ˜ํ…์ธ  ์ถ”๊ฐ€
shadowRoot.innerHTML = `
  <style>
    p {
      color: blue;
    }
  </style>
  <p>Hello, Shadow DOM!</p>
`;

Shadow DOM์˜ ๋ชจ๋“œ

Shadow DOM์€ ๋‘ ๊ฐ€์ง€ ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.

  • Open: Shadow DOM์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” API๊ฐ€ ์ œ๊ณต๋œ๋‹ค. ์™ธ๋ถ€์—์„œ Shadow DOM์˜ ๋‚ด์šฉ์„ ์ฝ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Closed: Shadow DOM์— ๋Œ€ํ•œ ์ ‘๊ทผ์ด ์ œํ•œ๋œ๋‹ค. ์™ธ๋ถ€์—์„œ Shadow DOM์˜ ๋‚ด์šฉ์„ ์ง์ ‘์ ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค.


Shadow DOM ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

1. Shadow root ์ƒ์„ฑํ•˜๊ธฐ

  <body>
    <div class="shadow-host">
      <a href="https://velog.io/@woogur29/posts"> ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ… ๋ฐ”๋กœ๊ฐ€๊ธฐ</a>
    </div>
    <script>
      const shadowHostElement = document.querySelector(".shadow-host");
	  // host์— Shadow DOM ๋ถ™์ด๊ธฐ์œ„ํ•ด attachShadow() ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ
      const shadowRoot = shadowHostElement.attachShadow({ mode: "open" });
    </script>
  </body>

์ด ์ฝ”๋“œ๋Š” Shadow host์˜ ์ž์‹ ์š”์†Œ์ธ ๋นˆ Shadow root๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. <html> ์š”์†Œ๊ฐ€ DOM์˜ ์‹œ์ž‘์ธ ๊ฒƒ ์ฒ˜๋Ÿผ Shadow root๋Š” Shadow DOM์˜ ์‹œ์ž‘์  ์—ญํ• ์„ ํ•œ๋‹ค.

์ผ๋ฐ˜ HTML ์ž์‹ ์š”์†Œ(<a>)๋Š” ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ํ™•์ธ๋˜๊ธด ํ•˜์ง€๋งŒ Shadow root๊ฐ€ ์ฐจ์ง€ํ•˜๋ฉด์„œ ๋” ์ด์ƒ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๊ฒŒ ๋œ๋‹ค.

2. Shadow tree๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์ฝ˜ํ…์ธ  ์ƒ์„ฑํ•˜๊ธฐ

Shadow tree๋Š” DOM tree์™€ ๋น„์Šทํ•˜์ง€๋งŒ ์ผ๋ฐ˜ DOM ๋Œ€์‹  Shadow DOM์„ ์‚ฌ์šฉํ•œ๋‹ค.

const link = document.createElement("a");
link.href = shadowHostElement.querySelector("a").href;
link.innerHTML = `${shadowHostElement.querySelector("a").textContent}`;
shadowRoot.appendChild(link);

์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•˜๊ฒŒ appendChild() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Shadow DOM์— ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

์ด ์‹œ์ ์—์„œ ํ•ด๋‹น ์š”์†Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

3. ์Šคํƒ€์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ

const styles = document.createElement("style");
styles.textContent = `
a {
  text-decoration: none;
  color: #20c997;
  font-weight: 800;
}`;
shadowRoot.appendChild(styles);


Shadow DOM์„ ์ด์šฉํ•œ ์Šคํƒ€์ผ๊ณผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฒฉ๋ฆฌ

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Shadow DOM ์˜ˆ์ œ</title>
    <style>
        /* ๋ถ€๋ชจ ์š”์†Œ์˜ CSS */
        .parent {
            background-color: red; /* ๋ถ€๋ชจ ๋ฐฐ๊ฒฝ์ƒ‰ */
            padding: 20px;
        }

        /* ๋ถ€๋ชจ์˜ ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
        .parent button {
            color: white;
            background-color: black;
        }
    </style>
</head>
<body>
    <div class="parent">
        <h1>๋ถ€๋ชจ ์š”์†Œ</h1>
        <button onclick="alert('๋ถ€๋ชจ ๋ฒ„ํŠผ ํด๋ฆญ!')">๋ถ€๋ชจ ๋ฒ„ํŠผ</button>

        <!-- Shadow DOM์„ ์‚ฌ์šฉํ•  ์š”์†Œ -->
        <div id="shadow-host"></div>
    </div>

    <script>
        // Shadow DOM ์ƒ์„ฑ
        const shadowHost = document.getElementById('shadow-host');
        const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

        // Shadow DOM ๋‚ด์˜ HTML ๋ฐ CSS ์ถ”๊ฐ€
        shadowRoot.innerHTML = `
            <style>
                /* Shadow DOM ๋‚ด์˜ CSS */
                .child {
                    background-color: blue; /* Shadow DOM ๋‚ด์˜ ๋ฐฐ๊ฒฝ์ƒ‰ */
                    color: white;
                    padding: 10px;
                    border: 1px solid black;
                }
            </style>
            <div class="child">
                <h2>Shadow DOM ์š”์†Œ</h2>
                <button id="shadow-button">Shadow DOM ๋ฒ„ํŠผ</button>
            </div>
        `;

        // Shadow DOM ๋‚ด์˜ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
        const shadowButton = shadowRoot.getElementById('shadow-button');
        shadowButton.addEventListener('click', () => {
            alert('Shadow DOM ๋ฒ„ํŠผ ํด๋ฆญ!');
        });
    </script>
</body>
</html>

์Šคํƒ€์ผ ๊ฒฉ๋ฆฌ

  • ์ผ๋ฐ˜ DOM: ๋ชจ๋“  ์Šคํƒ€์ผ์ด ์ „์—ญ์ ์œผ๋กœ ์ ์šฉ๋œ๋‹ค. ์ฆ‰, ๋ถ€๋ชจ ์š”์†Œ์˜ ์Šคํƒ€์ผ์ด ์ž์‹ ์š”์†Œ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋‹ค.

  • Shadow DOM: Shadow DOM ๋‚ด์—์„œ ์ •์˜ํ•œ ์Šคํƒ€์ผ์€ ํ•ด๋‹น DOM์—๋งŒ ์ ์šฉ๋œ๋‹ค. ์™ธ๋ถ€ ์Šคํƒ€์ผ์ด Shadow DOM ๋‚ด์˜ ์š”์†Œ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์Šคํƒ€์ผ์˜ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํฌ๋ฆฝํŠธ ๊ฒฉ๋ฆฌ

  • ์ผ๋ฐ˜ DOM: ๋ชจ๋“  ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์™€ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ „์—ญ์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. ์—ฌ๋Ÿฌ ์š”์†Œ๊ฐ€ ๊ฐ™์€ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๊ณต์œ ํ•  ๊ฒฝ์šฐ, ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋™์ผํ•œ ID๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Shadow DOM: Shadow DOM ๋‚ด์˜ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Š” ์™ธ๋ถ€ DOM๊ณผ ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. Shadow DOM ๋‚ด์—์„œ ์„ค์ •ํ•œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Š” ํ•ด๋‹น DOM์—๋งŒ ์ ์šฉ๋˜๋ฉด ์™ธ๋ถ€ DOM์˜ ์š”์†Œ์™€๋Š” ๋ณ„๊ฐœ๋กœ ์ž‘๋™ํ•œ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ์ผ๊ด€์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค.

์žฌ์‚ฌ์šฉ์„ฑ

  • ์ผ๋ฐ˜ DOM: UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์Šคํƒ€์ผ๊ณผ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ถฉ๋Œํ•˜์ง€ ์•Š๋„๋ก ์‹ ๊ฒฝ์จ์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Shadow DOM: Shadow DOM์„ ์‚ฌ์šฉํ•˜๋ฉด ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ๋…๋ฆฝ์ ์ธ ์Šคํƒ€์ผ๊ณผ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ, ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋‚˜ ์ปจํ…์ŠคํŠธ์—์„œ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.


์‚ฌ์šฉ ์‚ฌ๋ก€

  • UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: ๋ณต์žกํ•œ UI ์š”์†Œ(ex. button, modal, dropdown ๋“ฑ)๋ฅผ ๋งŒ๋“ค ๋•Œ Shadow DOM์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํƒ€์ผ๊ณผ ๋™์ž‘์„ ๋…๋ฆฝ์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์›น ์ปดํฌ๋„ŒํŠธ: ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›น ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ Shadow DOM์„ ํ†ตํ•ด ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ๊ณผ ๊ตฌ์กฐ๋ฅผ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“ ์ •๋ฆฌํ•˜๊ธฐ

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


๐Ÿ™ƒ ๋„์›€์ด ๋˜์—ˆ๋˜ ์ž๋ฃŒ๋“ค

shadow DOM ์‚ฌ์šฉํ•˜๊ธฐ - MDN
(๋ฒˆ์—ญ) Shadow DOM์€ ๋ฌด์—‡์ผ๊นŒ?

2๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2024๋…„ 8์›” 6์ผ

์ฒ˜์Œ ๋“ค์–ด๋ณธ ๊ฐœ๋…์ธ๋ฐ, ์Šคํƒ€์ผ ๊ฒฉ๋ฆฌ๋˜๋Š”๊ฒŒ ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ๋ฅผ ๊ฑด๋“œ๋ฆฌ๊ฒŒ ๋˜๋Š” ๋‚ ์—๋Š” ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™๋„ค์š”.
์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

1๊ฐœ์˜ ๋‹ต๊ธ€