솔직히 고백하자면, 나는 Web Components에 세 번 데였다. 2018년에 Polymer로 사이드 프로젝트 만들다가 Shadow DOM 스타일링에서 포기했고, 2021년에 Lit 1.0 시절 써봤다가 SSR 불가능이라는 벽에 부딪혔고, 2023년에는 팀원 설득에 실패했다. "그냥 React 쓰면 되잖아요."

그런데 2026년 4월, 상황이 좀 다르다.

Declarative Shadow DOM이 게임을 바꿨다

Web Components의 가장 큰 약점은 서버 사이드 렌더링이었다. Shadow DOM은 JavaScript가 실행되어야만 생성 가능했고, 이건 SEO와 초기 로딩 성능 모두에서 치명적이었다. Declarative Shadow DOM은 이 문제를 정면으로 해결한다.

HTML 안에서 직접 Shadow DOM을 선언할 수 있게 됐다:

<my-card>
  <template shadowrootmode="open">
    <style>
      :host { display: block; border: 1px solid #e0e0e0; padding: 16px; }
    </style>
    <slot></slot>
  </template>
  카드 내용이 여기에 들어간다
</my-card>

JavaScript 로드 전에도 스타일이 적용된 상태로 렌더링된다. Chrome, Edge, Safari 모두 지원하고, Firefox는 올해 초 지원을 시작했다. 브라우저 호환성이라는 마지막 변명이 사라진 셈이다.

Lit의 SSR 패키지(@lit-labs/ssr)도 안정화 단계에 접어들었다. 전체 DOM 에뮬레이션 없이 Lit의 선언적 템플릿 포맷을 활용해서 빠른 TTFB와 스트리밍을 지원한다. "Web Components는 서버 렌더링이 안 돼서 못 쓴다"는 2023년까지의 정설이 깨진 거다.

번들 사이즈, 숫자로 보자

React 18 기준 minified + gzipped 사이즈가 약 44kB다. react-dom까지 합치면 130kB를 넘긴다. 트리셰이킹을 잘 하면 줄일 수 있지만, "잘 했을 때" 이야기다.

Lit 3.x는 16kB. 올해 등장한 Elena라는 Progressive Web Components 라이브러리는 2.6kB다. React의 useState 훅 하나 번들에 포함시키는 비용보다 작을 수도 있는 크기로 컴포넌트 시스템 전체가 돌아간다.

Lighthouse 점수에 집착하는 사람으로서 이 숫자를 그냥 넘기기가 어렵다. 특히 모바일 3G 환경에서 130kB와 2.6kB의 차이는 사용자가 체감하는 로딩 시간에서 초 단위로 벌어진다.

그래서 React를 버리라는 건가?

아니다. 당연히 아니다.

Web Components는 "React 킬러"가 아니라 다른 레이어의 도구다. React나 Vue 안에서 커스텀 엘리먼트를 쓸 수 있고, Lit은 @lit/react 패키지로 React 래퍼를 공식 지원한다. 핵심은 프레임워크 간 호환성이다.

디자인 시스템을 생각해보자. 카카오스타일이 Vanilla Extract로 시스템을 재구축하면서 겪었던 문제 — 지면별로 UI가 미묘하게 달라지는 현상 — 를 Shadow DOM의 캡슐화가 구조적으로 방지한다. 스타일 격리가 브라우저 레벨에서 보장되니까 "이 컴포넌트는 어디에 갖다 놔도 똑같이 보인다"가 성립한다.

마이크로 프론트엔드 아키텍처에서는 더 명확하다. 팀 A는 React, 팀 B는 Vue, 팀 C는 Svelte를 쓰는 조직이라면? 공통 컴포넌트를 브라우저 네이티브 엘리먼트로 만들면 모든 팀이 의존성 충돌 없이 가져다 쓸 수 있다. 이론이 아니다 — Salesforce(Lightning Web Components), Adobe(Spectrum Web Components), GitHub(<relative-time> 같은 커스텀 엘리먼트)가 프로덕션에서 이미 검증한 패턴이다.

장밋빛만 칠하면 안 되니까

현실적인 제약도 짚자.

폼 연동이 여전히 까다롭다. ElementInternals API가 나오면서 나아졌지만, <form> 안에서 커스텀 엘리먼트의 밸리데이션 연동은 React Hook Form이나 Formik 같은 라이브러리의 DX에 한참 못 미친다. 폼이 핵심인 서비스라면 이 부분을 심각하게 고려해야 한다.

테스트 생태계가 얇다. Testing Library로 Shadow DOM 내부를 바로 쿼리할 수 없다. shadow-dom-testing-library 같은 서드파티에 의존해야 하는데, 메인테이너가 한두 명인 오픈소스에 프로덕션을 거는 건 늘 불안하다.

TypeScript 지원은 "거의" 좋다. Lit 자체의 타입 지원은 훌륭한데, 커스텀 엘리먼트를 JSX에서 쓸 때 타입 추론이 완벽하지 않다. declare module 보일러플레이트가 필요하고, props 자동완성은 React 컴포넌트만큼 매끄럽지 않다.

그래서 누가 써야 하나

Web Components는 2026년에 "관심만 가져봐" 단계를 졸업했다. 디자인 시스템 팀, 마이크로 프론트엔드를 운영하는 조직, 번들 크기에 목숨 거는 프로젝트에서는 진지한 검토 대상이다.

다만 "모든 걸 커스텀 엘리먼트로 다시 짜겠다"는 건 2018년의 나처럼 데이는 길이다. 기존 React 프로젝트에서 공통 UI를 하나씩 추출하는 점진적 접근이 현실적이다. 한 번에 혁명보다, 컴포넌트 하나씩 프레임워크로부터 독립시키는 전략. 결국 프론트엔드의 정답은 항상 "상황에 따라 다르다"인데, 올해의 브라우저 네이티브 컴포넌트는 그 "상황"의 범위가 꽤 넓어졌다.