Kang JiYeon
← Notes

Angular Host 위에 React 위젯을 Shadow DOM으로 격리하기

2026.02 · 5분 · Shadow DOM · MFE · Web Components · Architecture

React 통합 구조로 가기 전, 우리는 Angular Host 위에 React 위젯을 얹는 MFE 구조를 운영했다. 이 글은 그 위젯들을 Shadow DOM으로 격리해 만든 방식과, 그 경계가 남긴 비용에 대한 기록이다. (왜 이 구조를 결국 걷어냈는지는 MFE → React 전환기에서 다룬다.)

무엇을 만들었나

Angular Host(shell)가 인증과 공통 기능을 담당하고, 신규 화면은 React 위젯으로 개발했다. single-spa가 위젯을 마운트·언마운트했고, 각 React 위젯은 자체 Shadow Root 안에 마운트해 스타일을 격리했다.

왜 Shadow DOM이었나

서로 다른 스택·팀이 만든 위젯이 한 페이지에 공존하면 CSS 충돌이 큰 리스크다. 전역 클래스·리셋·폰트가 서로를 덮어쓴다. Shadow DOM은 스타일 캡슐화를 브라우저 레벨에서 제공한다 — 위젯의 CSS가 Shadow Root 밖으로 새지 않고, 바깥 스타일도 (상속되는 일부 속성을 제외하면) 안으로 들어오지 않는다. “위젯은 자기 스타일만 책임진다”는 경계를 강제로 그을 수 있었다.

가장 어려웠던 것: 경계를 넘는 인증

인증은 Angular Host가 담당했고, 격리된 React 위젯은 그 세션을 공유해야 했다. 프레임워크가 달라 상태를 직접 공유할 수 없어, 쿠키(공유 저장소)와 이벤트(CustomEvent + BroadcastChannel)로 인증을 동기화했다. 이 인증 아키텍처와, 이후 단독 운영으로 전환하며 그 계층을 걷어낸 이야기는 인증 아키텍처 가이드에서 자세히 다룬다.

경계가 남긴 비용

Shadow DOM은 격리를 주지만, 그 경계를 넘는 모든 것이 일이 된다:

  • 전역 스타일 주입 — Host의 디자인 토큰·폰트·공통 CSS가 Shadow Root 안으로 자동 상속되지 않는다. 필요한 스타일을 Shadow Root 안에 다시 넣어줘야 했다.
  • 이벤트 retargeting — Shadow 경계를 넘는 이벤트는 target이 Shadow Host로 재지정된다. 바깥에서 위젯 내부 요소를 직접 겨냥하는 로직은 예상과 다르게 동작했다.
  • 포커스·선택 — 포커스 이동, document.activeElement, 텍스트 선택이 Shadow 경계에서 달라진다.
  • 서드파티 라이브러리 — light DOM을 가정하는 라이브러리 (포털·툴팁·모달을 body에 붙이는 류)는 Shadow 안에서 스타일·위치가 어긋났다.

배운 것

  • Shadow DOM은 “스타일 격리”라는 한 가지 문제를 확실히 푼다. 하지만 격리는 공짜가 아니라, 경계를 넘는 모든 상호작용에 비용을 매긴다.
  • 상태 공유는 프레임워크가 아니라 플랫폼(BroadcastChannel 같은 브라우저 기본기)으로 풀면 스택에 묶이지 않는다. 대신 이벤트 모델의 한계(초기 상태·순서·레이스)를 직접 메워야 한다.
  • 격리가 정말 필요한지 먼저 묻는다. CSS 충돌이 실제 위협일 때는 값어치가 있지만, 그렇지 않으면 경계 비용이 더 크다. 이 비용이 쌓인 것이 이후 React 통합 구조로 전환한 이유 중 하나였다.