ETC -

Web Component 정리

  • -

Web Component 는 복잡해진 마크업 요소들을 코드의 충돌없이 재사용 가능하고,
캡슐 화 시키기 위한 목적으로 만들어 졌습니다.

WebComponent 의 장점으로는 아무래도 웹표준을 따르는 만큼 어떤 FE 프레임워크나 라이브러리를 사용하더라도 공유, 조합이 가능하다는 것 입니다.

캡슐화 되어있는 Web Component 는 현재 사용중인 테마, 프레임워크로 부터컨텐츠를 보호 할 수 있으며,
잘 만들어진 디자인 시스템으로 무장된 Web Component 는 재사용 할 기회가 굉장히 많아 집니다.

단점으로는 현재 나와있는 수 많은 프레임워크와 라이브러리의 데이터 관련 API 작성과
비교해서 Web Component 를 순수하게 만들때는조금 장황하고 복잡할 수 있습니다.

그런 부분을 해결하기 위해 lit-element, stencil, angular-element, polymer, vue 등의 도움을 주는
유틸 들이 존재 합니다.

이번 글에서는 WebComponent 를 이루는 기술 요소인 Template, slot 2개의 기술 요소에 대하여 간단하게 살펴봅시다.

1. Template

<template> 이용하여 구현하면 아래와 같은 모양이 된다.

CSS의 경우 cascading 의 특징 때문에 적용한 스타일이 다른 컴포넌트에도 영향을 미치지만 이런식으로 캡슐화 되면 다른 컴포넌트에 영향을 미치지 않는다.

<body>
  <item-component></item-component>

  <script type="module" src="./webComponent.js"></script>
</body>
// ./webComponent.js
class Hellow extends HTMLElement {
  constructor() {
    super();
    this.shadowObj = this.attachShadow({ mode: "open" });
    this._tpl = document.createElement("template");
  }
  render() {
    this._tpl.innerHTML = this.getTemplate();
    this.getStyle();
    this.shadowObj.appendChild(this._tpl.content.cloneNode(true));
  }

  getStyle(){
    const styleEl = document.createElement("style");
    styleEl.type = 'text/css';
    styleEl.innerHTML = `
      * { font-family: sans-serif; margin: 0; padding:0;}
      
      div { padding: 10px; border: 1px solid gray; border-radius: 10px; width: 200px; margin: 10px; }
      .inner .title {
        color: red
      }
      .inner .text {
        color: blue
      }
      .inner .description {
        color: yellow
      }
      @media (min-width: 768px){
        .inner .title {
          color: blue
        }
        .inner .text {
          color: yellow
        }
        .inner .description {
          color: red
        }
      }
    `;
    this.shadowRoot.appendChild(styleEl);
  }

  getTemplate() {
    return `
      <div class="inner">
        <div class="title">
        <p>template 사용법</p>
      </div>
      <div class="text">template 본문</div>
      <div class="description">
        template의 사용법을 서술 합니다. description
      </div>
    `;
  }

  connectedCallback() {
    this.render();
  }
}

customElements.define("item-component", Hellow);

다음과 같이 style 적용할때 선택자를 하위 선택자 식으로 (css와 동일한 선택자)방법을 할수 있습니다.

css와 동일한 선택자 방식

2. slot

<body>
  <item-component>
    <div slot="title" class="title">
      <p>slot 사용법</p>
    </div>
    <div slot="text" class="text">slot 본문</div>
    <div slot="description" class="description">
      slot의 사용법을 서술 합니다. description
    </div>
  </item-component>

  <script type="module" src="./webComponent.js"></script>
</body>
class Hellow extends HTMLElement {
  constructor() {
    super();
    this.shadowObj = this.attachShadow({ mode: "open" });
    this._tpl = document.createElement("template");
    this.styleEl = document.createElement("style");
  }
  render() {
    this._tpl.innerHTML = this.getTemplate();
    this.shadowObj.appendChild(this._tpl.content.cloneNode(true));
    this.shadowObj.appendChild(this.getStyle());
  }

  getStyle(){
    this.styleEl.type = 'text/css';
    this.styleEl.textContent = `
    ::slotted(*) { color: gray; font-family: sans-serif; }
      
    div { padding: 10px; border: 1px solid gray; border-radius: 10px; width: 200px; margin: 10px; }
    ::slotted(.title) {
      color: red
    }
    ::slotted(.text) {
      color: blue
    }
    ::slotted(.description) {
      color: yellow
    }
    @media (min-width: 768px){
      ::slotted(.title) {
        color: blue
      }
      ::slotted(.text) {
        color: yellow
      }
      ::slotted(.description) {
        color: red
      }
    }
  `;
  return this.styleEl;
  }

  getTemplate() {
    return `
      <div class="inner">
        <slot name="title"></slot>
        <slot name="text"></slot>
        <slot name="description"></slot>
      </div>
    `;
  }

  connectedCallback() {
    this.render();
  }
}

customElements.define("item-component", Hellow);

다음과 같이 style 적용할때 선택자를 하위 선택자 식으로 (css와 동일한 선택자)방법을 할수 없습니다. 
::slotted라는 선택자를 호출해서 갈호안에 상위요소 하나만 선택 가능하다.

slot을 사용할때 가상선택자를 활용

👉🏻마치며

shadow DOM과 함께 template을 이용하면 재사용성을 높인다는 것은 항상 고민해야 하는 부분이다.
이번에 template와, slot를 알아보면서 경우에따라 사용여부를 판단해야 하지 않을까 생각이 듭니다.
Template방석은 HTML에서 정보를 담당하지 않고, HTMLElement class에서 담당을 하기때문에 React 초창기 모습과 비슷합니다. 여기서 data(state)까지 다룬다고 하면 아마 유지보수 측면에서 힘들어 질것같습니다.
slot방식은 HTML에서 정보를 담당하고 있고, HTMLElement class에서 slot 태그에 name속성으로 연결고리를 만들어주고 있습니다. 다만 style 적용에서 하는데 있어서 자유로운 편은 아니라서 참고해야 할점들이 있습니다.

스타일 지정
웹 구성 요소와 shadow DOM 내부 요소에 스타일을 지정하는 다양한 방법이 있습니다. 
:host : shadow root로 지정된 웹 구성 요소에 스타일을 적용합니다. 
:host-context(<selector>) : 웹 구성 요소 혹은 상위 요소의 선택자가 <selector>와 일치하면, 웹 구성 요소의 자식 요소에 스타일을 적용합니다. 
::slotted(<compound-selector>) : 지정한 복합 선택자와 일치하는 슬롯 콘텐츠에 스타일을 적용합니다.

👉🏻파일공유

webComponent.zip
0.00MB

👉🏻참고자료

[웹 컴포넌트] Template and Slot

Composing Custom Elements With Slots And Named Slots

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.