
웹 컴포넌트는 <hello-world></hello-world>와 같은 커스텀 HTML 엘리먼트이다. 이름는 HTML 사양에서 공식적으로 지원되는 요소와 충돌하기 않도록 데시(-)가 꼭 포함되어야 한다.
엘리먼트를 컨트롤하기 위해서는 ES2015 class 를 정의해야 한다. 이름은 어떤 것으로든 지정할 수 있지만 HelloWorld는 연습용입니다. 모든 HTML 요소의 기본 속성과 메서드를 나타내는 HTMLElement 인터페이스를 확장해야 합니다.
이 예에서 요소의 텍스트는 'Hello World'로 설정됩니다.
클래스를 특정 요소에 대한 핸들러로 정의하려면 CustomElementRegistry에 클래스를 등록해야 합니다.
customElements.define('hello-world', HelloWorld);브라우저는 이제 JavaScript가 로드될 때 <hello-world> 엘리먼트와 HelloWorld 클래스를 연결합니다.
이 컴포넌트는 다른 일리먼트와 마찬가지로 CSS 로 스타일링할 수 있습니다.
hello-world {
  front-weight: bold;
  color: red;
}이 컴포넌트는 항상 같은 텍스트를 출력하기 때문에 유용하지 않습니다. 다른 일리먼트와 마찬가지로 HTML attributes를 추가할 수 있습니다.
<hello-world name="Craig"></hello-world>text를 override 하여 "Hello Craig!"를 보여주려고 한다.
HelloWorld 클래스에 constructor() 함수를 추가한다. constructor()는 각 객체가 생성될 때마다 실행한다.
1. super() 함수를 호출하여 부모 HTMLElement를 초기화하고
2. 다른 초기화를 만든다. 이번 케이스에선, 우리는 "World"로 설정된 name 속성을 정의할 것이다.
class HelloWorld extends HTMLElement {
  constructor() {
    super();
    this.name = 'World';
  }
  ...
}너의 컴포넌트는 name 속성에만 관심이 있습니다. static observedAttributes() 속성은 관찰할 속성 배열을 반환해야 합니다.
// component attributes
static get observedAttributes() {
  return ['name'];
}attributeChangedCallback() 메서드는 속성이 HTML에 정의되거나 JavaScript를 사용하여 변경될 때 호출됩니다. 속성 이름, 이전 값, 새 값이 전달됩니다.
// attribute change
attributeChangedCallback(property, oldValue, newValue) {
  if (oldValue === newValue) return;
  this[property] = newValue;
}이 예에서는 name 속성만 업데이트되지만 필요에 따라 속성을 추가할 수 있습니다.
마지막으로, connextedCallback() 메서드에서 메세지를 수정해야 합니다.
connectedCallback() {
  this.textContent = `Hello ${this.name}!`;
}브라우저는 웹 구성 요소의 생명 주기 동안 6가지 메서드를 자동으로 호출합니다. 위의 예에서 처음 4개를 이미 보았지만 여기 전체 목록이 있습니다.
document.querySelector('hello-world').setAttribute('name', 'Everyone');웹 컴포넌트는 JavaScript 프레임워크에서 찾을 수 없는 몇 가지 고유한 기능을 제공합니다.
위에서 구축한 웹 구성 요소가 작동하는 동안 외부 간섭에 영향을 받지 않으며 CSS 또는 JavaScript가 수정할 수 있습니다. 마찬가지로 구성 요소에 대해 정의한 스타일이 누출되어 다른 사람에게 영향을 줄 수 있습니다.
The Shadow DOM을 이용하여 웹 컴포넌트에 분리된 DOM을 첨부하여 이 문제를 해결합니다.
const shadow = this.attachShadow({ mode: 'closed' });모드는 다음과 같습니다.
Shadow DOM은 다른 DOM요소처럼 조작할 수 있습니다.
connectedCallback() {
  const shadow = this.attachShadow({ mode: 'closed' });
  
  shadow.innerHTML = `
    <style>
      p {
        text-align: center;
        font-weight: normal;
        padding: 1em;
        margin: 0 0 2em 0;
        background-color: #eee;
        border: 1px solid #666;
      }
    </style>
    <p>Hello ${ this.name }!</p>`;
}이제 구성요소가 <p> 요소 내부의 "Hello" 텍스트를 렌더링하고 스타일을 지정합니다. 글꼴 및 색상과 같은 일부 스타일은 명시적으로 정의되지 않았기 때문에 페이지에서 상속되지만 컴포넌트 외부의 JavaScript 또는 CSS로 수정할 수 없습니다.
CSS :host 선택자는 <hello-world> 웹 컴포넌트 내에서 외부 요소의 스타일링을 지정할 수 있습니다.
:host {
  transform: rotate(180deg);
}요소가 특정 클래스를 사용할 때 적용할 스타일을 설정할 수도 있습니다. 예시 <hello-world class="rorate90">:
:host(.rorate90) {
  transform: rotate(180deg);
}스크립트 내에서 HTML을 정의하는 것은 더 복잡한 웹 구성 요소에 대해 비실용적일 수 있습니다. 템플릿을 사용하면 웹 컴포넌트에서 사용할 수 있는 HTML 청크를 페이지에 정의할 수 있습니다. 여기에는 몇 가지 이점이 있습니다.
템플릿은 <template> 태그에 정의되며 컴포넌트 클래스 내에서 참조할 수 있도록 ID를 할당하는 것이 실용적입니다. 
<template id="hello-world">
  <style>
    p {
      text-align: center;
      font-weight: normal;
      padding: 0.5em;
      margin: 1px 0;
      background-color: #eee;
      border: 1px solid #666;
    }
  </style>
  <p class="hw-text"></p>
  <p class="hw-text"></p>
  <p class="hw-text"></p>
</template>웹 컴포넌트 클래스는 이 템플릿에 액세스하고 해당 콘텐츠를 가져오고 요소를 복제하여 사용되는 모든 곳에서 고유한 DOM 조각을 생성할 수 있습니다.
const template = document.getElementBy('hello-world').content.cloneNode(true);DOM을 수정하고 Shadow DOM에 직접 추가할 수 있습니다.
connectedCallback() {
  const
    shadow = this.attachShadow({ mode: 'closed' }),
    template = document.getElementById('hello-world').content.cloneNode(true),
    hwMsg = `Hello ${ this.name }`;
  
  Array.from( template.querySelectorAll('.hw-text'))
  	.forEach(n => n.textContent = hwMsg);
  
  shadow.append(template);
}슬롯을 사용하여 템플릿을 커스텀할 수 있습니다. <hello-world> 웹 컴포넌트를 사용하고 싶지만 <h1> 을 Shadow DOM 안에 넣는다면 다음과 같이 코드를 작성해야 합니다.
<hello-world name="Craig">
  <h1 slot="msgtext">Hello Default!</h1>
</hello-world>선택적으로 다른 단락과 같은 다른 요소를 추가할 수 있습니다.
<hello-world name="Craig">
  <h1 slot="msgtext">Hello Default!</h1>
  <p>This text will become part of the components.</p>
</hello-world>이제 템플릿 내에서 슬롯을 구현할 수 있습니다.
<template id="hello-world">
  <slot name="msgtext" class="hw-text"></slot>
  <slot></slot>
</template>"msgtext"로 설정된 <h1> 요소는 name이 "msgtext"인 <slot>이 있는 지점에 삽입됩니다. <p> 요소는 slot name이 할당되지 않았지만 unnamed <slot>에서 사용됩니다. 실제로 템플릿은 다음과 같습니다.
<template id="hello-world">
  <slot name="msgtext" class="hw-text">
    <h1 slot="msgtext">Hello Default!</h1>
  </slot>
  <slot>
    <p>This text will become part of the component.</p>
  </slot>
</template>실제로는 이렇게 간단하지 않습니다. <slot> Shadow DOM의 요소는 삽입된 요소를 가리킵니다. 내부 자식의 배열을 반환하는 .assignedNodes() 메서드를 사용해야만 <slot>을 찾아야만 엑세스할 수 있습니다. 
connectedCallback() {
  const
    shadow = this.attachShadow({ mode: 'closed' }),
    hwMsg = `Hello ${ this.name }`;
  // append shadow DOM
  shadow.append(
    document.getElementById('hello-world').content.cloneNode(true)
  );
  // find all slots with a hw-text class
  Array.from( shadow.querySelectorAll('slot.hw-text') )
    // update first assignedNode in slot
    .forEach( n => n.assignedNodes()[0].textContent = hwMsg );
}게다가, 웹 컴포넌트 내의 특정 슬롯을 대상으로 지정할 수 있지만 삽입된 요소의 스타일링을 직접 지정할 수는 없습니다.
<template id="hello-world">
  <style>
    slot[name="msgtext"] { color: green; }
  </style>
  <slot name="msgtext" class="hw-text"></slot>
  <slot></slot>
</template>템플릿 슬롯은 조금 특이하지만 한 가지 이점은 JavaScript가 실행되지 않는 경우 콘텐츠가 표시된다는 것입니다. 이 코드는 웹 컴포넌트 class가 성공적으로 실행될 때만 대체되는 기본 머리글과 단락을 보여줍니다.