Web Programming

Vanilla JS에서 웹 컴포넌트 생성, 사용하기

안녕하세요, 씨앤텍시스템즈 이나연 연구원입니다.

 

이번 글에서는 지난 글에 이어서 웹 컴포넌트를 만들고 사용하는 가장 기본적인 방법에 대해 정리해보겠습니다.

 

(이전 글)

2022.01.14 - [Web Programming/Design] - UI Component

 

UI Component

안녕하세요, 씨앤텍시스템즈 이나연 연구원입니다. 이번 글에서는 웹/앱의 UI 개발에서 Component에 대한 내용을 정리해 보겠습니다. UI에서의 컴포넌트 UI 컴포넌트를 개발하는 다양한 방법들 1. UI

cntechsystems.tistory.com

 

*이 글의 내용은 아래의 문서를 참고하여 작성되었습니다.

- https://ko.javascript.info/web-components

- https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks

 


내용

  1. 컴포넌트가 필요한 상황들
  2. window.customElement

1. 컴포넌트가 필요한 상황들

 

만약 웹 서비스 개발 중, 1초마다 현재 시간을 보여주는 시계를 html과 js를 이용하여 만든다면, 아래와 같이 만들 수 있을 겁니다.

<!doctype html>
<body>
  <p id="time"></p>
  
  <script>
	  const elem = document.getElementById("time");
	  const setDatetime = () => {
			const newDate = new Date();
			const hour = newDate.getHours();
			const minute = newDate.getMinutes();
			const second = newDate.getSeconds();
			
			elem.innerText = `${hour >= 12 ? '오후 '+hour : '오전 '+hour}:${minute}:${second}`;
	  };

	  window.onload = setDatetime;

	  setInterval(setDatetime, 1000);
  </script>
</body>

 

위 코드에서 만약 시간을 보여주는 방식이 다른 시계를 추가해야 하거나, 혹은 사용자가 원하는 대로 표시 방식이 변경되어야 한다면, elem.innerText에 들어갈 텍스트 형식을 하나하나 수정하고 다른 변수가 필요햘지도 모릅니다.

 
필요한 기능을 구현하는 코드의 재사용성을 높이고 보다 유연한 형태의 컴포넌트를 만들어 같은 기능의 시계를 만든다면, HTMLElement와 customElements 인터페이스를 이용하여 아래와 같이 시계를 만들 수 있습니다.

 

<!doctype html>
<body>
<script>
class TimeFormatted extends HTMLElement {

  render() {
    let date = new Date(this.getAttribute('datetime') || Date.now());

    this.innerHTML = new Intl.DateTimeFormat("default", {
      year: this.getAttribute('year') || undefined,
      month: this.getAttribute('month') || undefined,
      day: this.getAttribute('day') || undefined,
      hour: this.getAttribute('hour') || undefined,
      minute: this.getAttribute('minute') || undefined,
      second: this.getAttribute('second') || undefined,
      timeZoneName: this.getAttribute('time-zone-name') || undefined,
    }).format(date);
  }

  connectedCallback() {
    if (!this.rendered) {
      this.render();
      this.rendered = true;
    }
  }

  static get observedAttributes() {
    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    this.render();
  }

}

customElements.define("time-formatted", TimeFormatted);
</script>

<time-formatted id="elem" hour="numeric" minute="numeric" second="numeric"></time-formatted>

<script>
  setInterval(() => elem.setAttribute('datetime', new Date()), 1000);
</script>
</body>

 

구현된 화면

 

위에서 작성한 코드에서 다른 형식의 시계를 추가하고 싶다면, 만든 `<time-formatted></time-formatted>`를 사용하여 아래와 같이 html 코드만 추가하여 사용할 수 있습니다.

 

...
<time-formatted id="elem2" datetime="2019-12-01"
  year="numeric" month="long" day="numeric"
  hour="numeric" minute="numeric" second="numeric"
></time-formatted>

<script>
  ...
  setInterval(() => elem2.setAttribute('datetime', new Date()), 1000);
</script>

 

이렇게 생성한 custom elements에 css를 적용할 때는 기존 html 태그를 선택할 때와 마찬가지로 생성한 태그명으로 선택하여 css를 입힐 수 있습니다.

 

time-formatted {
  color: blue;
  font-weight: 700;
}

 

css가 추가된 요소

 


2. window.customElements

 

앞서 사용한 `customElements.define`의 경우 아래와 같이 파라미터를 가집니다.

 

customElements.define(name, constructor, options);

 

  • name : 이름은 새로 생성하는 custom element의 이름을 지정하고, 이 때 하이픈(-)을 반드시 포함해야 합니다.
  • constructor : 새로 생성하는 custom element의 생성자입니다.
  • options : custom element의 정의 방법을 제어하며, 필수 파라미터는 아닙니다. { extends: "ul" }와 같은 형태의 오브젝트를 사용하고, 예를 들어 { extends: "ul" }의 경우 기본 제공 엘리먼트 중 ul 요소를 확장하는 것을 의미합니다.

 

customElement를 사용할 때, 필수적인 생성자 이외에 아래와 같이 클래스 정의 내부에 해당 요소의 생명 주기별 몇가지 콜백을 추가로 정의할 수 있습니다. 

 

class MyElement extends HTMLElement {
  constructor() {
    super();
    // 요소 생성
  }

  connectedCallback() {
    // 이 사용자 정의 요소가 문서에 추가될 때, 브라우저가 이 콜백을 호출합니다.
    // (요소를 반복적으로 추가/삭제할 경우 여러 번 호출할 수 있습니다.)
  }

  disconnectedCallback() {
    // 이 사용자 정의 요소가 문서에서 삭제될 때, 브라우저가 이 콜백을 호출합니다.
    // (요소를 반복적으로 추가/삭제할 경우 여러 번 호출할 수 있습니다.)
  }

  static get observedAttributes() {
    return [/* 관찰하고자 하는 attribute의 이름을 요소로 하는 배열이 return 되어야 합니다. */];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 위에서 return된 이 사용자 정의 요소의 attribute 중 하나가 변경될 때마다 호출됩니다.
  }

  adoptedCallback() {
    // 사용자 정의 요소가 새로운 document로 이동되었을 때마다 호출됩니다.
    // (document.adoptNode에서 발생, 거의 사용되지 않음)
  }

  // 다른 요소 메서드와 속성이 있을 수 있습니다.
}

 

이렇게 window.customElements를 이용하여 생성한 custom element가 브라우저에게 어떻게 보일 것인지, 페이지에 추가되거나 삭제될 때 무엇을 할 것인지 등에 대해서 알려주는 역할을 하는 셈입니다.

 

아래 예시 코드를 통해 이를 확인해 볼 수 있습니다.

 

index.html
main.js (1)
main.js (2)

 

위 코드를 작성하여 브라우저로 문서를 열면 아래와 같은 화면이 나옵니다.

 

life-cycle-callbacks 예시 코드 화면

 

'Add custom-square to DOM' 버튼을 클릭하면, 아래와 같이 빨간색 상자가 생성되면서 attributeChangedCallback와 connectedCallback이 호출된 것을 알 수 있습니다. 

 

'Update attributes' 버튼을 클릭하면, 마찬가지로 attributeChangedCallback이 호출되고, 

 

'Remove custom-square from DOM' 버튼을 클릭하면, html에서 custom-square 요소가 사라지면서 아래와 같이 disconnectedCallback이 호출된 것을 확인 할 수 있습니다.

 


 

지금까지 사용자 정의 요소를 통해 컴포넌트를 만들고 사용하는 방법들에 대해 정리해 보았습니다.

더 많은 예제는 MDN에서 제공하는 에제들을 참고해 주시기 바랍니다.

 

 

사용자 정의 요소 사용하기 - 웹 컴포넌트 | MDN

웹 컴포넌트 표준의 주요 기능 중 하나는 사용자 정의 페이지 기능을 제공하는 길고 중첩된 요소들의 묶음으로 만족하는 것보다는, HTML 페이지에서 기능을 캡슐화하는 사용자 정의 요소를 생성

developer.mozilla.org

 


 

(참고 자료)

 

  • JAVASCRIPT.INFO - 웹 컴포넌트 :

https://ko.javascript.info/web-components

  • MDN Web Docs - Window.suctomElement : 
 

Window.customElements - Web API | MDN

Window 인터페이스의 customElements 읽기 전용 속성은 새로운 사용자 지정 요소를 등록하거나, 이전에 등록한 요소의 정보를 받아올 수 있는 CustomElementRegistry 객체의 참조를 반환합니다.

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/Web_Components/Using_custom_elements#%EC%83%9D%EB%AA%85_%EC%A3%BC%EA%B8%B0_%EC%BD%9C%EB%B0%B1_%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

사용자 정의 요소 사용하기 - 웹 컴포넌트 | MDN

웹 컴포넌트 표준의 주요 기능 중 하나는 사용자 정의 페이지 기능을 제공하는 길고 중첩된 요소들의 묶음으로 만족하는 것보다는, HTML 페이지에서 기능을 캡슐화하는 사용자 정의 요소를 생성

developer.mozilla.org

 

 

728x90