[JavaScript] 웹 컴포넌트

이윤우·2022년 9월 20일
0

JavaScript

목록 보기
5/34
post-thumbnail

Getting Started With Web Components

웹 컴포넌트는 <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;
}

Adding Attributes

이 컴포넌트는 항상 같은 텍스트를 출력하기 때문에 유용하지 않습니다. 다른 일리먼트와 마찬가지로 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}!`;
}

Lifecycle Methods

브라우저는 웹 구성 요소의 생명 주기 동안 6가지 메서드를 자동으로 호출합니다. 위의 예에서 처음 4개를 이미 보았지만 여기 전체 목록이 있습니다.

  • constructor()
    컴포넌트가 처음 초기화될 때 호출됩니다. super()를 호출해야 하며 기본값을 설정하거나 다른 사전 렌더링 프로세스를 수행할 수 있습니다.
  • static observedAttributes()
    브라우저가 관찰할 속성의 배열을 반환합니다.
  • attributeChangedCallback(propertyName, oldValue, newValue)
    관찰된 속성이 변경될 때마다 호출됩니다. HTML에 정의된 것은 즉시 전달되지만 JavaScript는 수정할 수 있습니다.
    document.querySelector('hello-world').setAttribute('name', 'Everyone');
  • connectedCallback()
    이 함수는 웹 구성요소가 문서 개체 모델에 추가될 때 호출됩니다. 필요한 렌더링을 실행해야 합니다.
  • disconnectedCallback()
    문서 개체 모델에서 웹 구성 요소가 제거될 때 호출됩니다. 이것은 저장된 상태를 제거하거나 Ajax 요청을 중단하는 것과 같이 정리가 필요한 경우에 유용할 수 있습니다.
  • adoptedCallback()
    이 함수는 웹 구성 요소가 한 문서에서 다른 문서로 이동할 때 호출됩니다.

How Web Components Interact With Other Elements

웹 컴포넌트는 JavaScript 프레임워크에서 찾을 수 없는 몇 가지 고유한 기능을 제공합니다.

The Shadow DOM

위에서 구축한 웹 구성 요소가 작동하는 동안 외부 간섭에 영향을 받지 않으며 CSS 또는 JavaScript가 수정할 수 있습니다. 마찬가지로 구성 요소에 대해 정의한 스타일이 누출되어 다른 사람에게 영향을 줄 수 있습니다.
The Shadow DOM을 이용하여 웹 컴포넌트에 분리된 DOM을 첨부하여 이 문제를 해결합니다.

const shadow = this.attachShadow({ mode: 'closed' });

모드는 다음과 같습니다.

  1. "open" - 외부 페이지의 JavaScript는 Shadow DOM에 액세스할 수 있습니다.
  2. "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 Templates

스크립트 내에서 HTML을 정의하는 것은 더 복잡한 웹 구성 요소에 대해 비실용적일 수 있습니다. 템플릿을 사용하면 웹 컴포넌트에서 사용할 수 있는 HTML 청크를 페이지에 정의할 수 있습니다. 여기에는 몇 가지 이점이 있습니다.

  1. JavaScript 내에서 문자열을 다시 작성할 필요 없이 HTML 코드를 조정할 수 있습니다.
  2. 각 유형에 대해 별도의 JavaScript class를 만들 필요 없이 컴포넌트를 커스터마이징할 수 있습니다.
  3. 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가 성공적으로 실행될 때만 대체되는 기본 머리글과 단락을 보여줍니다.

0개의 댓글