Web Components
Web Components는 Custom Elements, Shadow DOM, HTML Templates 세 가지 브라우저 표준 API를 조합해 프레임워크 없이 캡슐화된 재사용 UI 컴포넌트를 만드는 기술입니다. 어떤 HTML 환경에서도 동작하는 표준 기반 컴포넌트를 만들 수 있습니다.
▶아키텍처 다이어그램
🔍 구조 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
웹 앱을 만들다 보면 같은 버튼, 같은 모달, 같은 카드 레이아웃을 여러 프로젝트에서 다시 만드는 일이 반복됩니다. React 컴포넌트로 만들면 React가 없는 곳에서 쓸 수 없고, Vue로 만들면 다시 그 생태계에 묶입니다. 프레임워크를 바꾸거나 새 프로젝트를 시작할 때마다 같은 UI 로직을 다시 작성해야 했고, 한 페이지에 여러 프레임워크가 공존할 때는 스타일 충돌과 이벤트 버블링 문제가 뒤따랐습니다.
2011년 Google이 Polymer 프로젝트를 통해 웹 컴포넌트 아이디어를 처음 공개했을 때, 이는 특정 프레임워크 없이도 브라우저 표준만으로 컴포넌트를 만들 수 있어야 한다는 문제의식에서 시작됐습니다. 초기에는 브라우저 지원이 분산돼 있어 폴리필에 의존해야 했지만, 2018~2020년 사이 주요 브라우저가 Custom Elements v1과 Shadow DOM v1을 모두 표준으로 구현하면서 실용 단계에 접어들었습니다. 오늘날은 Lit(Google), FAST(Microsoft) 같은 경량 헬퍼 라이브러리가 보일러플레이트를 줄여주면서, 프레임워크 간 공유 컴포넌트나 임베드 위젯에 실질적으로 쓰이고 있습니다.
Web Components는 세 가지 독립적인 브라우저 API의 조합입니다. Custom Elements는 HTMLElement를 상속해 <my-button> 같은 사용자 정의 태그를 만들고, customElements.define()으로 브라우저 레지스트리에 등록합니다. connectedCallback, disconnectedCallback, attributeChangedCallback 같은 라이프사이클 훅으로 마운트·언마운트·속성 변화를 처리합니다. Shadow DOM은 this.attachShadow({ mode: 'open' })으로 외부와 격리된 DOM 트리를 생성합니다. 이 안의 CSS는 외부로 새지 않고, 외부 CSS도 안으로 들어오지 않습니다. 격리 경계는 스타일 충돌 없이 서드파티 환경에 임베드할 수 있게 만들어줍니다. HTML Templates는 <template> 태그로 DOM을 미리 파싱해 두되, 화면에 렌더링하지 않고 대기시키는 방식입니다. 필요할 때 cloneNode(true)로 복제해 Shadow DOM에 삽입합니다. <slot> 태그는 컴포넌트를 사용하는 쪽에서 넣어 준 콘텐츠를 컴포넌트 내부 특정 위치에 투영합니다.
Web Components와 React·Vue 같은 프레임워크 컴포넌트는 '재사용 가능한 UI 조각을 만든다'는 목표를 공유하지만 동작 방식이 다릅니다. React 컴포넌트는 Virtual DOM과 상태 관리 시스템 위에서 동작하며 React 런타임이 필요합니다. Web Components는 브라우저가 직접 이해하는 표준 API라 런타임 의존이 없고, 어떤 HTML 환경에서도 `<my-component>` 태그만으로 동작합니다. 반면 상태 관리와 반응형 업데이트는 Web Components 스펙 자체에 포함되어 있지 않아 직접 구현하거나 Lit 같은 헬퍼를 써야 합니다. Shadow DOM과 CSP(콘텐츠 보안 정책) 사이의 충돌도 알아둘 필요가 있습니다. Shadow DOM 안에서 인라인 스타일이나 스크립트를 사용하면 `unsafe-inline`이 없는 CSP 정책에서 차단될 수 있습니다. 특히 Constructable Stylesheets가 아닌 방식으로 스타일을 삽입하거나, 서드파티 위젯이 Shadow Root 안에서 `<style>` 태그를 직접 생성할 때 이 문제가 나타납니다. Shadow DOM이 스타일을 캡슐화해 준다고 CSP 없이도 충분하다고 생각하기 쉽지만, 두 메커니즘은 역할이 달라 함께 설계해야 합니다. Web Worker와의 차이도 명확합니다. Web Worker는 UI가 없는 백그라운드 스레드에서 연산을 격리하는 API고, Web Components는 DOM 레이어에서 UI를 캡슐화하는 API입니다. 둘은 서로 다른 문제를 다루며 병렬 사용 사례도 없습니다.
Web Components가 실제로 빛나는 상황은 여러 팀이나 여러 프레임워크에 걸쳐 같은 UI를 공유해야 할 때입니다. 한 회사에서 React로 만든 앱, Vue로 만든 어드민, 정적 HTML 마케팅 페이지가 동일한 디자인 시스템 컴포넌트를 써야 한다면 Web Components 기반 라이브러리가 유일하게 실용적인 선택지입니다. Google의 Material Web Components, Adobe Spectrum, FAST 같은 대형 디자인 시스템이 이 방식을 택한 이유도 같습니다. 임베드 위젯 시나리오에서도 유용합니다. 고객 채팅 위젯, 결제 버튼, 소셜 공유 버튼처럼 외부 사이트에 삽입되는 UI는 호스트 페이지의 CSS와 충돌하면 안 됩니다. Shadow DOM 격리가 이 문제를 브라우저 레벨에서 해결합니다. 단, 대규모 SPA처럼 복잡한 상태 관리와 세밀한 반응형 렌더링이 핵심인 앱에서는 Web Components 단독으로 React나 Vue를 대체하려 하면 작성 비용이 커집니다. Web Components는 '공유되어야 하는 UI 조각'에 맞고, '하나의 프레임워크 안에서 개발하는 앱 전체'에는 해당 프레임워크 컴포넌트 모델이 더 적합합니다.