ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Web Component 에 대하여 간략하게 알아봅시다.
    Javascript 2019. 4. 24. 23:57

     

    Web Component 는 복잡해진 마크업 요소들을 코드의 충돌없이 재사용 가능하고,

    캡슐 화 시키기 위한 목적으로 만들어 졌습니다.

    WebComponent 의 장점으로는 아무래도 웹표준을 따르는 만큼 어떤 FE 프레임워크나 라이브러리를 사용하더라도

    공유, 조합이 가능하다는 것 입니다.

    캡슐화 되어있는 Web Component 는 현재 사용중인 테마, 프레임워크로 부터컨텐츠를 보호 할 수 있으며,

    잘 만들어진 디자인 시스템으로 무장된 Web Component 는 재사용 할 기회가 굉장히 많아 집니다.

    최근 발표된 Apple Music Web 의 Beta 버전에는 ember 와 함께 stencil 을 활용한 Web component 가

    적절하게 사용 되었습니다.

    기존 웹보다 앱에 치중하던 애플이 이제는 웹에 좀 더 무게를 실으려는 움직임 일 수도 있고..

    Apple Music Web beta

    단점으로는 현재 나와있는 수 많은 프레임워크와 라이브러리의 데이터 관련 API 작성과

    비교해서 Web Component 를 순수하게 만들때는조금 장황하고 복잡할 수 있습니다.

    그런 부분을 해결하기 위해 lit-element, stencil, angular-element, polymer, vue 등의 도움을 주는

    유틸 들이 존재 합니다.

    이번 글에서는 WebComponent 를 이루는 기술 요소인 Template, ShadowDOM, CustomElement

    3개의 기술 요소에 대하여 간단하게 살펴봅시다.

     

    1. Template

    Template 의 뜻을 살펴보면 미리 작성된 형식을 가진 문서나 파일이라고 나와있습니다.

    말그대로 Template 은 미리 작성 해 둔 마크업 조각, 스크립트를 저장해두고 필요할 때 렌더링 시킬 수 있도록 해줍니다.

    Template 의 특징을 살펴봅시다.

    • Template 의 content 가 활성화 되기까지 기본적으로 Template 은 비활성화 상태이다.
    • Template 이 사용 되기 전까지 Template 내의 어떠한 스크립트나 마크업도 실행되지 않는다.
    • Template 이 활성화 되기 전 까지 메인페이지에서는 Template 의 자식 노드들에게 접근 할 수 없다.

     

    간단한 실행 예제를 봅시다.

    <!DOCTYPE html>
    <html lang="ko">
      <head>
        <meta charset="utf-8">
      </head>
      <body>
        <template>
          <h1>WC - Template</h1>
        </template>
      </body>
    </html>

     

    간단하게 Template 태그와 내부에 h1 태그를 삽입하였습니다.

    이제 해당 페이지를 실행해보면 빈 화면과 함께 ElementsTree 를 살펴보면

    Template 요소는 존재하지만 화면에 나타나지 않습니다.

    내부 자식들도 단순하게 document-fragment 로 랩핑 되어 있습니다.

    이제 해당 Template 를 활성화 시켜 봅시다.

    활성화 시키기 위해서는 Template 의 content 를 가져와서 붙여야 하는데

    가장 좋은 방법은 원본 content 를 deep-copy 한 복사본을 만들어 페이지에 붙이는 것 입니다.

    <!DOCTYPE html>
    <html lang="ko">
    
      <head>
        <meta charset="utf-8">
        <style>
          body, html {
            height: 100%;
          }
          #main {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100%;
          }
        </style>
      </head>
    
      <!-- Body, Main-->
      <body>
        <div id="main"></div>
      </body>
    
      <!-- Template -->
      <template id="wc-template">
        <h1>WC - Template</h1>
      </template>
    
      <!-- script -->
      <script>
        if ('content' in document.createElement('template')) {
          console.log('supports Template');
          var wctemplate = document.querySelector('#wc-template');
          var clone = document.importNode(wctemplate, true);
          document.getElementById('main').appendChild(clone.content);
        } else {
          console.log('stop');
        }
      </script>
    
    </html>

     

    현재 브라우저에서 Template 의 지원 여부를 확인하고 importNode 를 사용하여

    복사본을 만든 뒤 페이지에 붙이며 활성화를 시작했습니다.

    실행을 해보면 내부 요소가 나타나는 것을 볼 수 있습니다.

    화장실?

     

     

    2. Custom Element

    개발자가 별도의 이름을 가진 새로운 HTML 태그 를 생성하거나 기존에 있는 HTML 태그를 확장할 있도록 해주는 기능 입니다.

    CusomElement 는 ShadowDom 과 함께 사용하여 구성된 Web Component 의 모듈식 코드 사용과

    재사용성을 늘릴 수 있도록 해줍니다.

    CusomElement 는 CustomElementRegistry 객체를 사용하여 등록하게 되고,

    내부에는 lifecycle 이 존재합니다.

    CusomElement 를 정의하는 코드를 살펴봅시다.

     

    <!DOCTYPE html>
    <html lang="ko">
    
    <head></head>
    
    <body>
      <custom-ele title="WC"></custom-ele>
      <div>
        <button id="delete-wc">제거</button>
      </div>
    </body>
    <script>
      class CustomEle extends HTMLElement {
        // 생성자 에서는 보통 해당 엘리먼트에 포함된 함수를 초기화 한다.
        constructor() {
          super();
        }
        // 커스텀 엘리먼트가 생성될때 실행된다.
        connectedCallback() {
          this.render();
        }
        // 해당요소가 새로운 문서로 이동 될 때마다 호출 된다.
        adoptCallback() { }
        // 요소의 속성이 추가, 제거, 업데이트, 교체되는 부분을 관찰하고 호출된다.
        // 이때 observedAttributes 속성에 나열된 특성에서만 호출된다.
        attributeChangedCallback(attrName, oldVal, newVal) {
          this.render();
        }
        //attributeChangedCallback 에서 관찰하는 항목을 리턴한다.
        static get observedAttributes() {
          return ['title'];
        }
        get title() {
          return this.getAttribute('title');
        }
        // custom element 가 제거될때 호출된다.
        disconnectedCallback() {
          alert('bye bye');
        }
        // custom method
        render() {
          this.innerHTML = `
          <h1>${this.title}</h1>
         `
        }
      }
      window.customElements.define('custom-ele', CustomEle);
    </script>
    
    </html>

     

    life cycle hooks 을 보면

    • connectedCallback() : 커스텀 엘리먼트가 정의될때 실행된다.
    • adoptCallback(): 새로운 페이지나 문서로 이동될때 실행된다.
    • attributeChangedCallback(): 커스텀 엘리먼트 내에 있는 요소가 변경될때 해당 함수가 호출된다.
    • observedAttributes(): 위 attributeChangedCallback() 이 관찰 할 목록을 배열로 리턴하여 지정 해준다.
    • disconnectedCallback(): 커스텀 엘리먼트가 제거 될때 호출된다.

     

    실행해봅시다.

    그리고 개발자 도구에서 title 속성을 바꿔봅시다.

    title 이 변경되는 것을 볼 수 있습니다.

    attributeChangedCallback() 과 observedAttrobutes() 없이 실행하면 해당 속성 값을 변경해도 반영되지 않습니다.

    삭제 버튼을 눌러 커스텀 엘리먼트를 없애면 disconnectedCallback() 이 실행되는 것 또한 볼 수 있습니다.

    주의할 점은 custom elemet 의 이름에 꼭 ‘-’ 가 포함되어여 합니다.

    bye bye

     

     

    3. ShadowDOM

    쉐도우 돔은 마크업 구조와 스타일을 다른 코드 또는 컴포넌트, 메인페이지와 분리하여 충돌하지 않도록 하고,

    기본 DOM 구조에 별도로 첨부할 수 있도록 해줍니다.

    ShadowDOM 은 일반적으로 DOM 노드를 만들어 다른 요소의 자식으로 추가하는 것과는 달리,

    요소에 연결은 되지만 독립된 범위가 지정된 DOM 트리를 만듭니다.

    Shadow DOM Tree

    Shadow Host 가 생성되면 해당 DOM 트리의 shadow root 가 생성되고

    그밑으로 독립적으로 생성된 element 들이 존재하게 됩니다.

    ShaodwDOM 의 특징은

     

    • 자체적인 DOM 모델에서 작동한다. 일반적인 document.querySelector 로 Shaodw DOM 의 자식에 접근 할 수 없다.
    • Shadow DOM 의 스타일규칙 역시 범위가 해당 Shadow DOM 으로 국한되어 있다.
    • 브라우저가 이미 자체적으로 ShadowDOM 을 호출하는 있는 요소는 Shadow DOM 으로 호스팅 할 수 없다.

     

    그럼 위에서 만든 커스텀 엘리먼트를 Shadow DOM 으로 만들어 봅시다.

    class CustomEle extends HTMLElement {
    
        constructor() {
          super();
          this.attachShadow({ mode: 'open' });
          this.render();
        }
    
        attributeChangedCallback(attrName, oldVal, newVal) {
          this.render();
        }
    
        static get observedAttributes() {
          return ['title'];
        }
    
        disconnectedCallback() {
          alert('bye bye');
        }
    
        get title() {
          return this.getAttribute('title');
        }
    
        render() {
          this.shadowRoot.innerHTML =
            `<slot>
              <h1>${this.title}</h1>
             </slot>
          `;
        }
      }

     

    attachShadow 를 이용하여 Shadow Tree 를 생성하고 shadow root 에 요소들을 추가 하였습니다.

    slot 에 별도의 이름을 지정하여 미리 정해진 slot 으로 위치하게 만들 수 도 있습니다.

     

    <!DOCTYPE html>
    <html lang="ko">
    
      <head></head>
    
      <body>
        <custom-ele title="WC">
          <h1 slot="second">Custom Content</h1>
          <h2 slot="first">Custom Content</h1>
        </custom-ele>
      </body>
      <script>
        class CustomEle extends HTMLElement {
          constructor() {
            super();
            this.attachShadow({ mode: 'open' });
            this.shadowRoot.innerHTML = `
                <slot name="first"></slot>
                <slot name="second"></slot>
              `
          }
        }
        window.customElements.define('custom-ele', CustomEle);
      </script>
    
    </html>

     

    Style 같은 경우도 위 특징에 설명 했듯이 외부 스타일의 영향을 받지 않고 독립적으로 지정이 가능 합니다.

    <!DOCTYPE html>
    <html lang="ko">
    
      <head>
        <style>
          h1 {
            color: blue;
          }
        </style>
      </head>
    
      <body>
        <custom-ele title="WC">
          <h1 slot="second">Custom Content</h1>
          <h2 slot="first">Custom Content</h1>
        </custom-ele>
      </body>
      <script>
        class CustomEle extends HTMLElement {
          constructor() {
            super();
            this.attachShadow({ mode: 'open' });
            this.shadowRoot.innerHTML = `
              <style>h1{ color: red; }</style>
              <h1>Custom Content</h1>
              <slot name="first"></slot>
              <slot name="second"></slot>
            `
          }
        }
        window.customElements.define('custom-ele', CustomEle);
      </script>
    
    </html>

     

    ShadowDOM 의 스타일 접근에는 다양한 방법이 존재 합니다.

    ShadowDOM 의 다양한 접근 방법과 자세한 설명은 아래 구글 개발자 글 을 참조 하세요.!?

    https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=ko&source=post_page-----34e0f3ea8d99----------------------

     

    Shadow DOM v1: 자체 포함 웹 구성 요소  |  Web  |  Google Developers

    웹 개발자는 Shadow DOM을 사용하여 웹 구성 요소에 대한 구획화된 DOM 및 CSS를 만들 수 있습니다.

    developers.google.com

     

     

    대략적으로 Web Component 의 구성을 살펴보았습니다.

    사실 Web Component 관련 글은 아주 많고, 더 자세하고 상세하게 설명한 글 들도 많습니다.

    다 필요없고 위 링크한 구글 페이지만 가도 만족스럽게 공부가 가능합니다.

    아마 직접적으로 Web Component 를 순수하게 코딩해서 쓰는 곳은 많이 없다고 봅니다.

    이전 v0 때는 브라우저의 지원도 미미하고 WebComponent 만으로 구축하기에는 어려움이 상당히 많았습니다.

    현재 v1 에서는 그래도 많은 브라우저 들이 지원을 하는 추세고, 관련 라이브러리나 유틸도 많이 나오고 있습니다.

    좋은 도구들도 많고 웹표준을 이용한 개발도 점점 편리해지고 있으니 잘 활용 해 봅시다.

    댓글