[JavaScript] Web Components

Joowon Jang·2024년 8월 4일

JavaScript

목록 보기
15/17

Web Components

그 기능을 나머지 코드로부터 캡슐화하여 재사용 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용할 수 있도록 해주는 다양한 기술들의 모음

Custom elements, Shadow DOM, HTML template 이 세 가지 기술로 구성되며, 쉽게 말해, React에서 컴포넌트를 만들어 사용한 것과 비슷한 기능을 JavaScript 표준이 제공하는 것이다.

참고: 웹 컴포넌트 - Web API | MDN
https://developer.mozilla.org/ko/docs/Web/API/Web_components

사용법

Custom Element 선언

class MyElement extends HTMLElement {
  constructor() {
    super();
    // 엘리먼트가 생성됨
  }

  connectedCallback() {
    // 엘리먼트가 문서에 추가될 때 브라우저가 이 메서드를 호출합니다.
    // (엘리먼트가 반복적으로 추가/제거되면 여러 번 호출될 수 있음)
  }

  disconnectedCallback() {
    // 엘리먼트가 문서에서 제거될 때 브라우저가 이 메서드를 호출합니다.
    // (엘리먼트가 반복적으로 추가/제거되면 여러 번 호출될 수 있음)
  }

  static get observedAttributes() {
    return [/* 변경 사항을 모니터링할 속성 이름의 배열 */];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 위에 나열된 속성 중 하나가 수정될 때 호출됩니다.
  }

  adoptedCallback() {
    // 엘리먼트가 새 문서로 이동될 때 호출됩니다.
    // (document.adoptNode에서 사용되며 매우 드물게 사용됨)
  }

  // 다른 엘리먼트 메서드와 속성이 있을 수 있습니다.
}

// c-element 선언
customElements.define('c-element', MyElement);

shadow DOM, template과 함께 사용한 예시

const buttonTemplate = document.createElement('template');
testTemplate.innerHTML = `<button>버튼</button>`;

class MyButton extends HTMLElement {
  constructor() {
    super();
    
    this.attachShadow({mode:'open'});
    this.shadowRoot.appendChild(buttonTemplate.content.cloneNode(true));
  }
  
  connectedCallback() {
    console.log(this.id); // 'test-button'
  }
}

customElements.define('c-button', MyButton);
<div id="app">
  <c-button id="test-button">버튼</c-button>
</div>

Q. Web Components에서 shadowDOM을 사용하는 이유가 뭔가요?

웹 컴포넌트 안에서 쉐도우 돔을 사용하는 가장 큰 이유는 4가지 정도가 있습니다.

  1. 스타일 충돌
    • shadowDOM을 사용하지 않으면 web Component 내부의 스타일이 전역 스타일과 충돌할 가능성이 생깁니다. 반대로 바깥에 설정한 css 또한 컴포넌트 내부의 자식들이 사용될 오염의 문제가 발생할 수 있습니다.
  2. DOM 트리의 스코프 설정
    • shadowDOM을 사용한 DOM은 외부 스크립트가 접근하기 어렵습니다. 외부 자바스크립트 코드가 아닌 컴포넌트 안에서 하나의 모듈 형태로 핸들링이 가능합니다.
  3. 이벤트 버블링
    • shadowDOM을 사용하면 이벤트가 격리되어 버블링이 제한됩니다. (인포 글 마지막 파트 shadowDOM event 파트에 있습니다) 웹 컴포넌트 내부에서 발생하는 이벤트가 외부로 전파되는 것을 방지할 수 있습니다.
  4. 재사용성과 캡슐화
    • 결국 webComponent를 사용하는 이유는 재사용 가능한 컴포넌트를 만들기 위한 것입니다. shadowDOM을 사용하지 않으면 스타일과 DOM 구조의 캡슐화가 어렵고, 다른 프로젝트나 다른 페이지에서 재사용할 때 문제가 발생할 수 있습니다.

특정 요소를 상속한 Custom Elements

class MyButton extends HTMLButtonElement {
  constructor() {
    super();
  }
  
  // ...
}

customElements.define('c-button', Button, { extends: 'button' });
<div id="app">
  <button is="c-button" id="test-button">버튼</button>
</div>

이렇게 HTMLButtonElement를 상속했다면, customElements.define에서 extends: button이라고 쓰고, 사용할 때에 <c-button>으로 사용하지 못하고 <button>요소에 is 속성으로 어떤 Custom element인지 적어줘야 한다.

속성 감시

observedAttributes를 통해 data-count속성을 감시하고 그 값이 변경되면, attributeChangedCallback 메서드가 호출된다.

class MyButton extends HTMLElement {
  constructor() {
    super();
    this.button = document.querySelector('button');
  }
  
  connectedCallback() {
    this._render();
  }
  
  static get observedAttributes() {
    return ['data-count'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(name, oldValue, newValue);
    if (oldValue !== newValue) {
      this._render();
    }
  }

  _render() {
    this.button.textContent = this.getAttribute('data-count');
  }
}

customElements.define('c-button', MyButton);

const customButton = document.querySelector("c-button");
let count= 0;

cutomButton.addEventListener("click", () => {
  c.setAttribute("data-count", ++count);
})

(버튼을 클릭할 때마다 버튼에 적힌 count가 1씩 증가하는 코드)

profile
깊이 공부하는 웹개발자

0개의 댓글