TypeScript에서의 Shadow DOM

TypeScript에서의 Shadow DOM

이 글은 TypeScript에서의 Shadow DOM에 대해 설명합니다.

우리는 Shadow DOM의 기초부터 실제 활용법까지 모두 자세히 설명하고, 실습에 사용할 수 있는 샘플 코드도 제공합니다.

YouTube Video

typescript-html-shadow-dom.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>TypeScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    h1, h2 {
 32        font-size: 1.2rem;
 33        color: #007bff;
 34        margin-top: 0.5em;
 35        margin-bottom: 0.5em;
 36        border-left: 5px solid #007bff;
 37        padding-left: 0.6em;
 38        background-color: #e9f2ff;
 39    }
 40
 41    button {
 42        display: block;
 43        margin: 1em auto;
 44        padding: 0.75em 1.5em;
 45        font-size: 1rem;
 46        background-color: #007bff;
 47        color: white;
 48        border: none;
 49        border-radius: 6px;
 50        cursor: pointer;
 51        transition: background-color 0.3s ease;
 52    }
 53
 54    button:hover {
 55        background-color: #0056b3;
 56    }
 57
 58    #output {
 59        margin-top: 1em;
 60        background-color: #1e1e1e;
 61        color: #0f0;
 62        padding: 1em;
 63        border-radius: 8px;
 64        min-height: 200px;
 65        font-family: Consolas, monospace;
 66        font-size: 0.95rem;
 67        overflow-y: auto;
 68        white-space: pre-wrap;
 69    }
 70
 71    .highlight {
 72        outline: 3px solid #ffc107; /* yellow border */
 73        background-color: #fff8e1;  /* soft yellow background */
 74        transition: background-color 0.3s ease, outline 0.3s ease;
 75    }
 76
 77    .active {
 78        background-color: #28a745; /* green background */
 79        color: #fff;
 80        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 81        transition: background-color 0.3s ease, box-shadow 0.3s ease;
 82    }
 83  </style>
 84</head>
 85<body>
 86    <div class="container">
 87        <h2>HTML Sample</h2>
 88        <div id="content"></div>
 89    </div>
 90
 91    <div class="container">
 92        <h1>JavaScript Console</h1>
 93        <button id="executeBtn">Execute</button>
 94        <div id="output"></div>
 95    </div>
 96
 97    <script>
 98        // Override console.log to display messages in the #output element
 99        (function () {
100            const originalLog = console.log;
101            console.log = function (...args) {
102                originalLog.apply(console, args);
103                const output = document.getElementById('output');
104                output.textContent += args.map(String).join(' ') + '\n';
105            };
106        })();
107
108        document.getElementById('executeBtn').addEventListener('click', () => {
109            // Prevent multiple loads
110            if (document.getElementById('externalScript')) return;
111
112            const script = document.createElement('script');
113            script.src = '/out/main.js';
114            script.id = 'externalScript';
115            //script.onload = () => console.log('typescript-html-shadow-dom.js loaded and executed.');
116            //script.onerror = () => console.log('Failed to load typescript-html-shadow-dom.js.');
117            document.body.appendChild(script);
118        });
119    </script>
120</body>
121</html>

Shadow DOM의 상세 설명과 단계별 가이드

Shadow DOM은 웹 컴포넌트의 핵심 요소 중 하나입니다. 이는 컴포넌트의 스타일과 구조를 외부로부터 분리하는 캡슐화된 DOM 트리를 생성합니다. 여기에서는 Shadow DOM의 기본부터 실제 사용 사례까지 자세히 설명하며, 예제 코드를 통해 직접 학습할 수 있습니다.

Shadow DOM이란 무엇인가요?

Shadow DOM은 다음과 같은 특성을 가진 웹 표준 기술입니다.

  1. 캡슐화

    Shadow DOM은 컴포넌트의 내부 DOM 구조를 외부로부터 분리합니다. 다른 스타일 및 스크립트와 간섭하지 않으며, 재사용성을 향상시킵니다.

  2. 독립적인 스타일 범위

    Shadow DOM 내부의 스타일은 외부 CSS에 영향을 미치지 않습니다. 마찬가지로, 외부 스타일은 Shadow DOM 내부에 적용되지 않습니다.

  3. 분리된 DOM 트리

    Shadow DOM은 일반 DOM과 분리된 트리로 존재하며, 부모 DOM으로부터 접근이 제한됩니다.

Shadow DOM의 기본 사용법

다음 코드는 Shadow DOM을 사용한 첫 번째 예제입니다.

 1class MyElement extends HTMLElement {
 2  constructor() {
 3    super();
 4
 5    // Attach Shadow DOM
 6    const shadowRoot = this.attachShadow({ mode: 'open' });
 7
 8    // Add HTML and CSS inside Shadow DOM
 9    shadowRoot.innerHTML = `
10      <style>
11        p {
12          color: blue;
13          font-size: 18px;
14        }
15      </style>
16      <p>This is inside Shadow DOM!</p>
17    `;
18  }
19}
20
21// Register the custom element
22customElements.define('my-element', MyElement);
23
24document.getElementById('content').innerHTML = `
25  <my-element></my-element>
26`;
  • 브라우저는 파란 텍스트 'This is inside Shadow DOM!'을 표시합니다. 이 텍스트의 스타일은 외부 CSS의 영향을 받지 않습니다.

Shadow DOM의 기본 단계

Shadow DOM을 사용하려면, 이 코드에서 보이는 것처럼 JavaScript에서 attachShadow 메서드를 사용하면 됩니다. 다음은 기본 단계입니다:.

  1. 커스텀 요소 생성 커스텀 엘리먼트는 표준 HTML 태그 외에 사용자가 직접 정의할 수 있는 태그입니다. 이 단계에서는 MyElement 클래스처럼 HTMLElement를 상속받는 클래스를 만들어, 브라우저가 이를 새로운 태그로 인식할 수 있도록 준비합니다. 생성된 클래스를 customElements.define()에 등록하면, HTML에서 이를 커스텀 태그로 사용할 수 있습니다.

  2. Shadow DOM 연결 커스텀 엘리먼트 내부에서 this.attachShadow()를 실행하면 Shadow DOM을 만들 수 있습니다.

  3. Shadow DOM 내부에 HTML 및 CSS 추가 Shadow DOM 안에서 자신만의 HTML 구조와 스타일을 정의할 수 있습니다. 예를 들어, innerHTML에 HTML과 CSS를 설정하면, 외부 CSS나 HTML의 영향을 받지 않는 독립적인 모양과 동작을 부여할 수 있습니다. 이것을 통해 캡슐화된 컴포넌트를 만들 수 있습니다.

Shadow DOM의 모드: openclosed

Shadow DOM에는 openclosed라는 두 가지 모드가 있습니다.

  • Open 모드: Shadow DOM에 연결된 shadowRoot는 외부에서 접근할 수 있습니다.
  • Closed 모드: Shadow DOM에 연결된 shadowRoot는 외부에서 접근할 수 없습니다.

아래는 두 가지 모드 간의 차이를 보여주는 코드 예제입니다.

 1class OpenElement extends HTMLElement {
 2  constructor() {
 3    super();
 4    this.attachShadow({ mode: 'open' }).innerHTML = `
 5      <p>Open Shadow DOM</p>
 6    `;
 7  }
 8}
 9
10class ClosedElement extends HTMLElement {
11  constructor() {
12    super();
13    this.attachShadow({ mode: 'closed' }).innerHTML = `
14      <p>Closed Shadow DOM</p>
15    `;
16  }
17}
18
19customElements.define('open-element', OpenElement);
20customElements.define('closed-element', ClosedElement);
21
22document.getElementById('content').innerHTML = `
23  <open-element></open-element>
24  <closed-element></closed-element>
25`;
26
27const openElement = document.querySelector('open-element') as OpenElement;
28console.log(openElement.shadowRoot); // ShadowRootが出力される
29
30const closedElement = document.querySelector('closed-element') as ClosedElement;
31console.log(closedElement.shadowRoot); // nullが出力される
  • closed 모드를 선택하면 shadowRoot 속성에 접근할 수 없습니다.

Shadow DOM을 사용한 스타일 캡슐화

Shadow DOM을 사용하면 컴포넌트 내에서 스타일을 완전히 캡슐화할 수 있습니다.

다음 예제는 전역 스타일과 Shadow DOM 내 스타일의 분리를 보여줍니다.

 1class StyledElement extends HTMLElement {
 2  constructor() {
 3    super();
 4    const shadowRoot = this.attachShadow({ mode: 'open' });
 5
 6    shadowRoot.innerHTML = `
 7      <style>
 8        p {
 9          background-color: lightblue;
10          padding: 10px;
11          border: 1px solid blue;
12        }
13      </style>
14      <p>Shadow DOM Styled Content</p>
15    `;
16  }
17}
18
19customElements.define('styled-element', StyledElement);
20
21document.getElementById('content').innerHTML = `
22  <style>
23    p {
24      color: red;
25      font-weight: bold;
26    }
27  </style>
28
29  <styled-element></styled-element>
30`;
  • Shadow DOM 내부의 p 요소는 전역 스타일의 영향을 받지 않으며 고유한 스타일이 적용됩니다.

Shadow DOM의 실용적인 예: 커스텀 툴팁

다음으로, Shadow DOM을 사용하여 커스텀 툴팁을 만드는 예제를 소개합니다.

 1class Tooltip extends HTMLElement {
 2  constructor() {
 3    super();
 4
 5    const shadowRoot = this.attachShadow({ mode: 'open' });
 6
 7    shadowRoot.innerHTML = `
 8      <style>
 9        :host {
10          position: relative;
11          display: inline-block;
12          cursor: pointer;
13        }
14
15        .tooltip {
16          visibility: hidden;
17          background-color: black;
18          color: white;
19          text-align: center;
20          padding: 5px;
21          border-radius: 5px;
22          position: absolute;
23          bottom: 125%;
24          left: 50%;
25          transform: translateX(-50%);
26          white-space: nowrap;
27        }
28
29        :host(:hover) .tooltip {
30          visibility: visible;
31        }
32      </style>
33      <slot></slot>
34      <div class="tooltip">Tooltip text</div>
35    `;
36  }
37}
38
39customElements.define('custom-tooltip', Tooltip);
40
41document.getElementById('content').innerHTML = `
42  <custom-tooltip>
43    Hover over me
44    <span slot="tooltip">This is a custom tooltip!</span>
45  </custom-tooltip>
46`;
  • 이 코드는 커스텀 툴팁을 생성하며, 호버 시 스타일이 적용된 툴팁을 표시합니다.

요약

Shadow DOM은 캡슐화된 DOM과 스타일 범위를 제공하는 웹 컴포넌트를 위한 중요한 기술입니다. 여기에서는 Shadow DOM의 기본, 사용법, 모드 차이, 스타일 캡슐화, 실용적인 예제를 다뤘습니다. 이러한 기능을 활용해서 재사용 가능하고 견고한 컴포넌트를 만들 수 있습니다.

위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.

YouTube Video