3 분 소요

Web components - shadow DOM

Web components

기능들을 캡슐화한 요소/태그/컴포넌트

웹 컴포넌트를 구성할 때 반복성을 줄이고 재사용성을 높이기 위해 사용할 수 있는 컴포넌트

알아야 할 세 가지 개념

Custom elements

요소와 동작들을 커스텀하여 정의할 수 있는 API set
(여러 개의 API를 세트로 사용해서 구현하니까 이렇게 표현한 듯)

Shadow DOM

메인 도큐먼트의 DOM 과는 별개로 렌더되는 캡슐화된 DOM 트리를 요소에 붙일 수 있게 하는 API set.

DOM 내부에 충돌 없이 style과 script를 적용할 수 있게 된다.

HTML templates

렌더되는 페이지에 보여지지 않는 마크업 태그들을 작성할 수 있는 요소/태그.

여기에 작성해둔 요소들을 재사용한다.

왜 씀?

  • 기본 요소들의 브라우저 통일성이 떨어짐.
    • 요소/태그들의 look and feel이 브라우저 혹은 환경(window, mac) 마다 너무 다 다르다.
      이미지
    • 그래서 통일화된 컴포넌트를 보여주기 위해 Javascript component를 사용한다. (script로 커스텀 컴포넌트를 추가/제거)
  • Javascript component는 느림.
    • 컴포넌트가 복잡하면 복잡할수록 상당한 크기의 javascript 덩어리.
    • javascript이기 때문에 리소스 로드가 다 완료된 후 동작 및 적용하므로 느림.
  • 기본 요소들은 기능이 다양하지 않음.
    • 느리니까 빠르게 하려면 HTML 을 써야 하는데, 아무리 요새 HTML 태그들 기능이 많아졌다고는 하지만 한계.
    • 그래서 개발자가 HTML 엘리먼트를 만들 수 있게 해주는 것이다.

특징

  • 여러 기능들 및 요소들을 하나에 다 갈아 넣어 캡슐화.
    하나의 캡슐로 쉽게 적용할 수 있게 한다.
  • 네이티브 요소로 동작 → 빠르고 성능 좋음.

쓰는 방법

Cutom elements

window.customElements 를 사용
(interface: CustomElementRegistry)

customElements.define 메서드

  • element name: kebab-case로 작성해야 함. 하나의 단어여서는 안됨.
  • class object: 요소의 동작이 정의된 클래스
  • options: extends 속성을 포함한 옵션 객체 ({ extends: "p" })

Cutom elements 종류

  • Autonomous custom elements: 자발적 / 독립형 커스텀 요소

    표준 HTML 요소에서 상속받는 것이 아닌 아예 새롭게 생성한 요소
    ex) <custom-modal/>

    customElements.define("popup-info", PopupInfo);
    
  • Customized built-in elements: 커스텀 빌트인 요소

    표준 HTML 요소를 상속받아 정의한 요소.

    ex) <p is=”all-red” ></p>

    customElements.define("all-red", AllRed, { extends: "p" });
    
  • 라이프 사이클 콜백 사용 가능

    • connectedCallback: 커스텀 요소가 요소에 append가 될 때마다 동작하게 하는 메서드
    • attributeChangedCallback: 커스텀 요소가 추가/제거/변경될 때마다 동작하게 하는 메서드
    • 등등 더 많다. (참고)

Shadow DOM

숨겨둔 DOM 트리를 실제 DOM 트리에 갖다 붙일 수 있도록 해준다.

shadow DOM tree는 메인 DOM 트리와 별개의 shadow root로 시작한다.
이 아래로 모든 아무 요소들을 실제 DOM 처럼 갖다 붙이면 된다.

밖에서는 Shadow DOM 안에 있는 요소들을 가지고 놀 수 있지만,
Shadow DOM 안에서는 밖에 있는 것들을 건들 수 없다.

const shadowOpen = elementRef.attachShadow({ mode: "open" });
const shadowClosed = elementRef.attachShadow({ mode: "closed" });

open을 하면 myCustomElem.shadowRoot 를 통해 DOM에서 shadow DOM에 접근 가능해진다.

closed는 DOM에서 shadow DOM에 접근할 수 없도록 한 것.
저렇게 접근해도 null 나옴 (video element 같은 것들이 closed되어 있는 경우)

해보기

class CustomModal extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" }); // open mode로 DOM에서 shadow DOM 접근 가능하도록 shadow root 설정

    const wrapper = document.createElement("div");
    wrapper.setAttribute("class", "wrapper");

    const closeButton = document.createElement("button");
    closeButton.innerText = "X";
    closeButton.setAttribute("class", "closeButton");
    closeButton.setAttribute("tabindex", 0);

    wrapper.appendChild(closeButton);

    const content = document.createElement("span");
    content.setAttribute("class", "content");
    content.textContent = this.getAttribute("data-text");

    wrapper.appendChild(content);

    this.shadowRoot.appendChild(wrapper); // wrapper만 shadow DOM에 추가

    const style = document.createElement("style");
    style.textContent = `
      .wrapper {
        width: 150px;
        height: 200px;
        border-radius: 8px;
        background-color: blue;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }

      .closeButton {
        background-color: red;
        color: white;
        border: none;
        padding: 4px 8px;
        position: absolute;
        top: 8px;
        right: 8px;
      }

      .content {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 150px;
        color: gray;
      }
    `;

    this.shadowRoot.appendChild(style); // style을 shadow DOM에 추가
  }
}
  • 이렇게 style을 안에서 지정할 수도 있고,
  • css link 태그를 이용해서 외부에 있는 stylesheet를 가져와서 적용시킬 수도 있다.
customElements.define("custom-modal", CustomModal);

이렇게 define 하면 <custom-modal /> 을 사용할 수 있게 된다.

Web components in JS lib/framework

React나 Vue에서 쓰나? 왜 쓰는 것이고, 어떻게 활용할 수 있나?

[인프랩 참고]

기존에 있던 레거시 css 들이 전역적으로 적용되어 있는 앱이 있다고 하자.

여기서 새로운 마이크로 서비스 혹은 도메인을 추가한다고 했을 때, 전역적으로 적용된 스타일들이 방해를 할 때가 있다.

ex) reset.css 들 엄청 다 달라서 갈아 엎기 참 힘든 친구. 이런 애들 처럼 못 갈아 엎겠는데, 그 산하에 스타일을 적용한다고 하면 여간 짜증나는 상황이 생길 수도..

그래서 react-shadow라는 라이브러리도 있다.

이를 이용해서 컴포넌트들(큰 컴포넌트 일 수도 있음)을 shadow DOM 산하로 가져와서 새출발할 수 있게 도와준다.

참고 링크

https://developer.mozilla.org/en-US/docs/Web/API/Web_components

https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements

https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM

https://tech.inflab.com/202208-shadow-root/

https://d2.naver.com/helloworld/188655


태그: ,

카테고리:

업데이트:

댓글남기기