Zeno ZENO

[Part 2] Web Components — Custom Elements 완전 정복

customElements.define()으로 나만의 HTML 태그를 만드는 방법. Autonomous vs Customized built-in elements 차이와 실전 예제를 다룹니다.

[Part 2] Web Components — Custom Elements 완전 정복

Part 1에서는 Web Components가 무엇인지, 왜 등장했는지, 그리고 어떤 핵심 기술들로 구성되어 있는지 알아봤다.

이번 글에서는 Web Components의 가장 기본이 되는 Custom Elements를 자세히 살펴본다.

Custom Elements를 이해하면 Web Components의 절반은 이해했다고 볼 수 있다. 왜냐하면 Web Components의 출발점이 바로 직접 HTML 태그를 만드는 것이기 때문이다.

1. Custom Elements란?

Custom Elements는 개발자가 직접 HTML 태그를 정의할 수 있게 해주는 브라우저 표준 기술이다.

HTML에는 기본적으로 브라우저가 미리 정해둔 태그들이 있다.

<div></div>
<button></button>
<input />
<section></section>
<article></article>

이런 태그들은 브라우저가 기본적으로 알고 있다.

하지만 Custom Elements를 사용하면 개발자가 직접 새로운 태그를 만들 수 있다.

<my-button></my-button>
<user-card></user-card>
<product-item></product-item>

여기서 <my-button>, <user-card>, <product-item>은 HTML 기본 태그가 아니다.

개발자가 직접 정의한 커스텀 태그다.

정리하면 Custom Elements는 브라우저에게 새로운 HTML 태그를 알려주는 기술이다.

2. 왜 직접 태그를 만들까?

웹 페이지를 만들다 보면 같은 UI를 여러 번 사용하게 된다.

  • 버튼
  • 카드
  • 상품 박스
  • 프로필 영역
  • 알림 메시지
  • 모달

예를 들어 사용자 카드 UI를 만든다고 해보자.

<article>
  <h3>사용자 이름</h3>
  <p>사용자 설명</p>
</article>

이 구조를 여러 곳에서 반복해서 사용한다면 매번 같은 HTML을 작성해야 한다.

Custom Elements를 사용하면 이 구조를 하나의 태그로 만들 수 있다.

<user-card></user-card>

이렇게 하면 HTML을 읽는 사람도 이 요소가 사용자 카드라는 것을 쉽게 알 수 있다.

즉 Custom Elements는 단순히 코드를 줄이는 목적만 있는 것이 아니라, HTML 구조를 더 의미 있게 만들기 위해서도 사용된다.

3. 커스텀 태그 이름 규칙

Custom Elements를 만들 때 가장 먼저 알아야 할 규칙이 있다.

커스텀 태그 이름에는 반드시 하이픈(-)이 들어가야 한다.

가능한 이름은 다음과 같다.

<my-button></my-button>
<user-card></user-card>
<product-item></product-item>
<app-modal></app-modal>

반대로 다음 이름은 사용할 수 없다.

<mybutton></mybutton>
<usercard></usercard>
<card></card>
<modal></modal>

하이픈이 필요한 이유는 브라우저 기본 태그와 구분하기 위해서다.

예를 들어 미래에 HTML 표준에 <card>라는 태그가 추가될 수도 있다. 그러면 개발자가 만든 <card>와 브라우저 기본 <card>가 충돌할 수 있다.

그래서 브라우저는 커스텀 태그 이름에 하이픈을 강제한다.

4. HTMLElement란?

Custom Element를 만들 때는 JavaScript class를 사용한다.

그리고 그 class는 보통 HTMLElement를 상속받는다.

class MyButton extends HTMLElement {

}

HTMLElement는 브라우저가 제공하는 기본 HTML 요소 클래스다.

쉽게 말하면 <div>, <button>, <section> 같은 HTML 요소들이 공통으로 가지는 기본 기능을 담고 있는 부모 개념이다.

JavaScript에서 HTML 요소를 선택하면 객체처럼 다룰 수 있다.

const button = document.querySelector('button');

console.log(button instanceof HTMLElement);

button instanceof HTMLElement는 선택한 button 요소가 HTMLElement를 기반으로 만들어졌는지 확인하는 코드다.

Custom Element도 결국 HTML 요소처럼 동작해야 하므로 HTMLElement를 상속받는다.

5. extends란?

extends는 JavaScript class에서 다른 class의 기능을 물려받을 때 사용한다.

class Animal {
  move() {
    console.log('움직입니다.');
  }
}

class Dog extends Animal {
  bark() {
    console.log('멍멍');
  }
}

const dog = new Dog();

dog.move();
dog.bark();

Dog extends Animal은 Dog가 Animal의 기능을 물려받는다는 뜻이다.

그래서 Dog에는 직접 만든 bark()뿐만 아니라 Animal에서 물려받은 move()도 있다.

Custom Element에서는 다음처럼 사용한다.

class MyButton extends HTMLElement {

}

이 코드는 MyButton이 HTML 요소로 동작하기 위해 HTMLElement의 기본 기능을 물려받는다는 뜻이다.

6. customElements.define()

class만 만든다고 커스텀 태그가 바로 동작하지는 않는다.

브라우저에게 “이 태그 이름은 이 class와 연결된다”라고 등록해야 한다.

이때 사용하는 API가 customElements.define()이다.

customElements.define('my-button', MyButton);

첫 번째 값은 사용할 태그 이름이다.

두 번째 값은 해당 태그와 연결할 class다.

즉 위 코드는 브라우저에게 이렇게 알려주는 것이다.

my-button 태그를 만나면 MyButton 클래스를 사용해라.

7. connectedCallback()

connectedCallback()은 Custom Element의 생명주기 메서드 중 하나다.

생명주기 메서드는 특정 시점에 브라우저가 자동으로 실행해주는 메서드다.

connectedCallback()은 커스텀 요소가 HTML 문서에 추가될 때 실행된다.

class MyButton extends HTMLElement {
  connectedCallback() {
    console.log('화면에 추가됨');
  }
}

customElements.define('my-button', MyButton);

HTML에 다음 태그가 있으면

<my-button></my-button>

브라우저가 이 요소를 문서에 추가하면서 connectedCallback()을 실행한다.

보통 connectedCallback() 안에서 화면에 표시할 HTML을 만든다.

8. 첫 번째 Custom Element 만들기

이제 직접 간단한 버튼 컴포넌트를 만들어보자.

class MyButton extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `
      <button>확인</button>
    `;
  }
}

customElements.define('my-button', MyButton);

HTML에서는 이렇게 사용한다.

<my-button></my-button>

브라우저는 <my-button>을 보고 MyButton 클래스를 실행한다.

그리고 connectedCallback() 안에서 this.innerHTML을 통해 내부에 실제 button 요소를 넣는다.

코드 리뷰

class MyButton extends HTMLElement는 MyButton이라는 커스텀 요소 클래스를 만든다.

connectedCallback()은 요소가 화면에 추가될 때 실행된다.

this는 현재 커스텀 요소인 <my-button>을 의미한다.

this.innerHTML은 현재 요소 내부에 HTML을 삽입한다.

customElements.define('my-button', MyButton)<my-button> 태그와 MyButton 클래스를 연결한다.

9. Attribute로 값 전달하기

커스텀 태그도 일반 HTML 태그처럼 attribute를 받을 수 있다.

<user-card name="사용자" role="관리자"></user-card>

여기서 namerole은 attribute다.

JavaScript에서는 getAttribute()로 값을 가져올 수 있다.

class UserCard extends HTMLElement {
  connectedCallback() {
    const name = this.getAttribute('name');
    const role = this.getAttribute('role');

    this.innerHTML = `
      <article>
        <h3>${name}</h3>
        <p>${role}</p>
      </article>
    `;
  }
}

customElements.define('user-card', UserCard);

이제 HTML에서 다음처럼 사용할 수 있다.

<user-card name="사용자" role="관리자"></user-card>

화면에는 사용자 이름과 역할이 출력된다.

코드 리뷰

this.getAttribute('name')은 현재 요소의 name attribute 값을 가져온다.

this.getAttribute('role')은 role attribute 값을 가져온다.

${name}${role}은 가져온 값을 HTML 문자열 안에 넣는 문법이다.

10. 같은 컴포넌트 여러 번 사용하기

Custom Element는 한 번 정의하면 여러 번 사용할 수 있다.

<user-card name="사용자 A" role="관리자"></user-card>
<user-card name="사용자 B" role="일반 회원"></user-card>
<user-card name="사용자 C" role="게스트"></user-card>

<user-card>는 자기 attribute 값을 읽어서 다른 내용을 출력한다.

이것이 컴포넌트 재사용의 핵심이다.

11. 중복 등록 주의

같은 이름의 Custom Element는 한 번만 등록할 수 있다.

customElements.define('my-button', MyButton);
customElements.define('my-button', MyButton);

이렇게 같은 이름으로 두 번 등록하면 에러가 발생한다.

개발 중에 파일이 여러 번 로드되거나, 같은 코드가 중복 실행되면 이런 문제가 생길 수 있다.

이를 방지하려면 등록 여부를 먼저 확인할 수 있다.

if (!customElements.get('my-button')) {
  customElements.define('my-button', MyButton);
}

customElements.get('my-button')은 이미 등록된 커스텀 요소가 있는지 확인한다.

등록되어 있지 않을 때만 define()을 실행하면 중복 등록 에러를 피할 수 있다.

12. Custom Elements에서 자주 하는 실수

Custom Elements를 사용할 때 자주 하는 실수는 다음과 같다.

  • 태그 이름에 하이픈을 넣지 않는다.
  • class만 만들고 customElements.define()을 호출하지 않는다.
  • 같은 태그 이름을 중복 등록한다.
  • attribute 값을 가져오기 전에 null 처리를 하지 않는다.
  • connectedCallback()이 여러 번 실행될 수 있다는 점을 고려하지 않는다.

특히 태그 이름 규칙과 중복 등록 문제는 처음 만들 때 자주 발생한다.

13. 정리

Custom Elements는 Web Components의 가장 기본이 되는 기술이다.

  • Custom Elements는 직접 HTML 태그를 만드는 기술이다.
  • 커스텀 태그 이름에는 반드시 하이픈이 필요하다.
  • 커스텀 요소 class는 HTMLElement를 상속받는다.
  • customElements.define()으로 태그 이름과 class를 연결한다.
  • connectedCallback()은 요소가 DOM에 추가될 때 실행된다.
  • getAttribute()로 attribute 값을 가져올 수 있다.
  • 한 번 정의한 커스텀 요소는 여러 번 재사용할 수 있다.

Custom Elements를 이해하면 Web Components의 기본 구조를 이해할 수 있다.

다음 Part에서는 Shadow DOM을 알아본다.

Shadow DOM은 컴포넌트 내부 구조와 스타일을 외부와 분리해서 스타일 충돌을 줄여주는 핵심 기술이다.

AD

제휴 광고

일부 링크는 제휴 링크이며, 구매 또는 가입 시 일정 수수료를 받을 수 있습니다.

AD

'Web components' 카테고리의 다른 글

전체보기