핵심 요약
Web Components는 네이티브 브라우저 API 3종 세트다. React·Vue 없이도 재사용 UI 컴포넌트를 만들 수 있다. 디자인 시스템을 여러 프레임워크에 동시에 배포해야 할 때 가장 유리하다.
- Custom Elements: 태그 정의
- Shadow DOM: 스타일·DOM 캡슐화
- Templates & Slots: 콘텐츠 투영
기본 컴포넌트
class UiButton extends HTMLElement {
static formAssociated = true;
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
button { padding: 8px 16px; border-radius: 8px; }
:host([variant="primary"]) button { background: #2563eb; color: white; }
</style>
<button><slot></slot></button>
`;
}
connectedCallback() {
this.shadowRoot.querySelector('button')
.addEventListener('click', () => this.dispatchEvent(new CustomEvent('ui-click')));
}
}
customElements.define('ui-button', UiButton);
<ui-button variant="primary">저장</ui-button>
Shadow DOM 스타일링
:host: 호스트 요소 자체 스타일:host([attr]): 속성 조건부::slotted(selector): 슬롯된 콘텐츠::part(name): 외부에서 내부 요소 스타일링 허용- CSS 변수는 Shadow 경계를 넘어감 (테마링에 활용)
Form Internals (form-associated)
폼과 자연스럽게 통합되는 커스텀 입력을 만들 수 있다.
class UiInput extends HTMLElement {
static formAssociated = true;
#internals;
constructor() {
super();
this.#internals = this.attachInternals();
this.attachShadow({ mode: 'open' }).innerHTML = `<input>`;
this.shadowRoot.querySelector('input')
.addEventListener('input', (e) => {
this.#internals.setFormValue(e.target.value);
});
}
static get observedAttributes() { return ['required']; }
}
customElements.define('ui-input', UiInput);
이제 <form> 안에서 FormData로 값이 제출된다.
React와의 상호운용
- React 19부터 custom element props·event가 기본 지원
- 그 이전은 ref로 속성·이벤트 바인딩
- Vue·Svelte·Angular 모두 호환
실전 디자인 시스템 전략
- 디자인 토큰(CSS 변수) → 전역 테마
- ::part 공개 범위를 문서에 명시 (API 안정성)
- i18n은 slot으로 외부 주입
- 번들: 런타임은 없음, 단일
.js모듈로 배포
자주 묻는 질문
React에서 써도 성능 이점이 있나?
컴포넌트 단위 성능보다 프레임워크 중립성이 핵심 가치. 여러 플랫폼(Next.js, 레거시 jQuery, Vue)에 같은 UI를 공급할 때 유리.
SSR 지원은?
Declarative Shadow DOM으로 SSR 가능. Next·Lit SSR이 대표적.
댓글 0