Zeno ZENO

[Part 8] Web Components — Form 연동과 ElementInternals

ElementInternals API로 커스텀 엘리먼트를 네이티브 form과 연동하는 방법. formAssociated, setFormValue, validity 구현 패턴을 다룹니다.

[Part 8] Web Components — Form 연동과 ElementInternals

Part 7에서는 Web Components의 고급 스타일링 전략을 알아봤다.

이번 글에서는 Web Components를 form과 연동하는 방법을 알아본다.

form은 사용자가 입력한 데이터를 서버로 보내거나 JavaScript에서 처리할 때 사용하는 HTML 요소다.

로그인, 회원가입, 검색, 주문서 같은 화면에서 자주 사용된다.

1. 기본 form 이해하기

일반적인 form은 다음처럼 작성한다.

<form>
  <input name="username" value="user1" />
  <button type="submit">전송</button>
</form>

inputname은 데이터의 이름이다.

value는 실제 값이다.

form이 제출되면 username=user1 같은 형태로 데이터가 전달된다.

2. 커스텀 요소는 기본적으로 form에 포함되지 않는다

직접 만든 커스텀 요소는 기본적으로 form 데이터에 자동 포함되지 않는다.

<form>
  <my-input name="username"></my-input>
  <button type="submit">전송</button>
</form>

이렇게 작성해도 <my-input>은 일반 input처럼 form 데이터에 자동으로 들어가지 않는다.

브라우저 입장에서는 <my-input>이 실제 입력 요소인지 알 수 없기 때문이다.

3. Form-associated Custom Elements

커스텀 요소를 form과 연결하려면 form-associated custom element로 만들어야 한다.

이를 위해 class에 다음 설정을 추가한다.

static formAssociated = true;

이 코드는 이 커스텀 요소가 form과 연결될 수 있다는 의미다.

class MyInput extends HTMLElement {
  static formAssociated = true;
}

customElements.define('my-input', MyInput);

4. ElementInternals란?

ElementInternals는 커스텀 요소가 form과 연결되기 위해 사용하는 내부 제어 객체다.

이 객체를 사용하면 커스텀 요소도 form에 값을 전달할 수 있다.

ElementInternals는 attachInternals()로 만든다.

class MyInput extends HTMLElement {
  static formAssociated = true;

  constructor() {
    super();

    this.internals = this.attachInternals();
  }
}

customElements.define('my-input', MyInput);

this.attachInternals()는 현재 커스텀 요소와 연결된 내부 제어 객체를 만든다.

5. setFormValue()

form에 전달할 값을 설정할 때는 setFormValue()를 사용한다.

this.internals.setFormValue('hello');

이 코드는 커스텀 요소의 form 제출 값을 hello로 설정한다.

6. 간단한 my-input 만들기

AD

제휴 광고 · 일부 링크는 수수료를 받을 수 있습니다

톨루나 팝업패널리워드 회원가입

class MyInput extends HTMLElement {
  static formAssociated = true;

  constructor() {
    super();

    this.internals = this.attachInternals();
  }

  connectedCallback() {
    this.innerHTML = `
      <input type="text" />
    `;

    const input = this.querySelector('input');

    input.addEventListener('input', () => {
      this.internals.setFormValue(input.value);
    });
  }
}

customElements.define('my-input', MyInput);

HTML에서는 다음처럼 사용한다.

<form>
  <my-input name="username"></my-input>
  <button type="submit">전송</button>
</form>

사용자가 내부 input에 값을 입력하면 setFormValue()가 그 값을 form에 전달할 값으로 설정한다.

7. name 속성의 역할

form에서 name은 데이터의 key 역할을 한다.

<input name="username" value="user1" />

이 경우 form 데이터는 username이라는 이름으로 전달된다.

커스텀 요소도 마찬가지로 name이 있어야 form 데이터에서 어떤 이름으로 전달될지 정해진다.

<my-input name="username"></my-input>

8. value 프로퍼티 만들기

input처럼 사용하려면 value 프로퍼티를 만들어두는 것이 좋다.

class MyInput extends HTMLElement {
  static formAssociated = true;

  constructor() {
    super();

    this.internals = this.attachInternals();
    this._value = '';
  }

  get value() {
    return this._value;
  }

  set value(newValue) {
    this._value = newValue;

    this.internals.setFormValue(newValue);
  }
}

get value()는 값을 읽을 때 실행된다.

set value()는 값을 설정할 때 실행된다.

this._value는 컴포넌트 내부에서 관리하는 값이다.

9. 유효성 검증

form에서는 required 같은 검증이 중요하다.

ElementInternals는 유효성 상태도 설정할 수 있다.

this.internals.setValidity(
  {
    valueMissing: true
  },
  '값을 입력해주세요.'
);

값이 정상일 때는 빈 객체를 전달한다.

this.internals.setValidity({});

이 기능을 사용하면 커스텀 요소도 브라우저의 form validation 흐름에 참여할 수 있다.

10. 언제 ElementInternals를 사용할까?

ElementInternals는 모든 Web Component에 필요한 것은 아니다.

주로 form과 직접 연동되어야 하는 컴포넌트에서 사용한다.

  • 커스텀 input
  • 커스텀 checkbox
  • 커스텀 radio
  • 커스텀 select
  • 커스텀 date picker
  • 커스텀 file uploader

단순 버튼, 카드, 모달에는 보통 필요하지 않다.

11. 정리

커스텀 요소는 기본적으로 form에 자동 연동되지 않는다.

  • form과 연동하려면 static formAssociated = true를 사용한다.
  • attachInternals()로 ElementInternals 객체를 만든다.
  • setFormValue()로 form에 제출될 값을 설정한다.
  • name attribute는 form 데이터의 key 역할을 한다.
  • setValidity()로 유효성 상태를 설정할 수 있다.

다음 Part에서는 static template 패턴과 메모리 최적화를 알아본다.

AD

제휴 광고

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

AD

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

전체보기