jhhan의 블로그

shadow DOM & template으로 HTML 모듈화 본문

Javascript

shadow DOM & template으로 HTML 모듈화

jhhan000 2024. 2. 18. 23:00

이번 포스트에서는 shadow DOM과 template에 대해서 알아보겠습니다.

지난 포스트와 이어지는 내용이 있으니 참고하셔도 좋습니다.

https://jhhan009.tistory.com/130

 

Web Components

이번 시간에는 Web Components에 대해서 알아보겠습니다. html 코드를 작성하다 보면, 혹은 살펴본다면 div 태그들을 많이 쓰는 것을 볼 수 있습니다. 근데 실제로 이 div 태그들을 계속해서 쓰는 것이

jhhan009.tistory.com

 

1.

화면 상에서는 나름 복잡하게 보이는데 코드 상으로는 간단한 그런 것들이 있습니다.

<input type="range" />

input 이 아마도 그런 예시가 될 것 같습니다.

코드 한 줄로 표현이 되지만, 과연 코드 한 줄로 저런 화면이 나오는 것이 되는 걸까요...

 

실제 쓰인 것을 보고 싶다면 다음과 같이 하면 됩니다.

  • '개발자 도구'를 열고
  • '설정' 을 누르고
  • show user agent shadow DOM 을 누르면 됩니다.

그러면 다음과 같이 볼 수 있습니다.

오! input 태그 안에 여러 개의 div로 감싸져 있는 것을 확인할 수 있습니다.

 

이런 것을 shadow DOM이라고 부릅니다.

일반적으로는 볼 수 없는 숨겨진 HTML입니다.

이런 것들을 한 번 만들어 보겠습니다.

 

2.

예시용으로 간단하게 만들겠습니다.

<script>
document.querySelector('#shadow').attachShadow({ mode: 'open' });
document.querySelector('#shadow').shadowRoot.innerHTML = `
    <div>
        <p style="color: blue;">shadow dom Example</p>
    </div>
`;
</script>

<html>
    <div id="shadow"></div>
</html>

  1. div와 p 태그가 들어가게 했습니다.
    그리고 구분을 위해서 글자 색깔은 blue로 해봤습니다.
  2. attachShadow({ mode: 'open' })를 써서 shadow Root 라는 공간을 하나 만들었고
  3. shadow Root 밑에 원하는 html을 넣으면 됩니다.

 

이런 식으로 한다면 shadow Root를 쓸 수 있습니다.

근데 굳이 이런 식으로 해야 하나 라는 생각이 들 수 있습니다.

이것만 봐서는 딱히 이점이 보이지 않기 때문입니다.

 

3.

shadow Root는 전에 배웠던 Web Component랑 같이 쓰면 유용하게 쓸 수 있습니다.

// js code
class LabelAndInput extends HTMLElement {
    connectedCallback() {
        let name = this.getAttribute('name');
        let plhd = this.getAttribute('placeholder');
        let value = this.getAttribute('value') || "";
        this.innerHTML = `
            <label>${name} input : </label>
            <input type="text" placeholder="${plhd}" value="${value}"/>
            <style>label { color: red; }</style>
        `;
    }
}

customElements.define('label-input', LabelAndInput);

// html code
<label-input name="email" placeholder="이메일을 입력해주세요." class="lblip1"></label-input>
<br/>
<label-input name="password" placeholder="비밀번호를 입력해주세요."></label-input>
<br/><br/>
<label>range</label>

  • Web Components 를 써서 진행해봤습니다.
  • 제가 원하는 것은 Web Component로 진행하는 label에만 style이 적용되는 것이지만
  • 결과는 모든 label에 적용되는 것을 확인할 수 있습니다.
  • Web Component에만 style이 적용되게 하려면?
    -> 이럴 때 shadow DOM을 쓰면 됩니다.

 

class LabelAndInput extends HTMLElement {
    connectedCallback() {
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
            <label>input : </label>
            <input />
            <style>label { color: red; }</style>
        `;
    }
}

customElements.define('label-input', LabelAndInput);

Web Components 안에만 style이 적용되는 것을 확인할 수 있습니다. 

 

그렇기 때문에 대부분 Web Components를 만들 때는 shadow Root를 활용해서 만든다고 합니다.

그래야 진정한 HTML 모듈화가 가능하기 때문입니다.

 

4.

근데 innerHTML에 들어갈 내용이 너무 많아진다면 스크립트에 모든 것을 적는 것이 부담스러울 수도 있습니다.

그럴 때는 template을 사용해서 진행하면 됩니다.

// js code
class LabelAndInput extends HTMLElement {
    connectedCallback() {
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.append(template1.content.cloneNode(true));
        let el = this.shadowRoot.querySelector('label');
        el.addEventListener('click', function() {
            console.log('label click ~');
        });
    }
}

customElements.define('label-input', LabelAndInput);

// html code
<label-input></label-input>

<template id="template1">
    <label>input : </label>
    <input />
    <style scoped>
        label { color: red; }
    </style>
</template>
  • template 태그는 특별한 태그로 화면 상에서는 렌더링 되지 않습니다.
    : 그래서 template 태그 안에서 원하는 html 코드를 보관합니다.
  • this.shadowRoot.append(template1.content.cloneNode(true));
    : 이것을 사용해서 HTML에 붙입니다.
  • 결과는 위와 같이 동일하게 나오게 됩니다.
  • EventListner가 필요하면 위와 같은 방식으로 추가해보면 됩니다.

 

이렇게 개발한다면 나중에는 원하는 class만 export해서 가져다가 쓰면

컴포넌트 모듈식으로 개발이 가능해질 것입니다.

 

 

5.

근데 이런 Web Components는 보셨으면 아시겠지만 생각보다 귀찮은 경우가 있습니다.

이런 것과 관련해서 Library가 있으니 해당 라이브러리를 쓰는 것도 괜찮을 것 같습니다.

Lit, Stencil 등의 라이브러리가 있다고 합니다.

알아보면 좋을 것 같습니다.

 

 

 

이렇게 해서 shadow DOM 과 HTML 모듈화에 대해서 알아봤습니다.

처음부터 하는 것도 괜찮겠지만, 익숙해진다면 라이브러리를 쓰는 것도 괜찮을 것 같습니다.

 

 

 

이렇게 해서 포스트를 마무리 하겠습니다.

 

 

 

출처: 코딩애플 - 쉽게 이해하는 JavaScript 객체지향 & ES6 신문법

'Javascript' 카테고리의 다른 글

Web Components  (0) 2024.02.17
웹브라우저 동작 원리 알기(stack, queue)  (0) 2024.02.08
js - async & await  (0) 2024.01.19
js - Promise  (0) 2024.01.19
동기, 비동기 처리 & 콜백함수  (1) 2024.01.13