WebComponent
외부 React 또는 Vue 컴포넌트를 Shadow DOM에 로드하여 렌더링하는 컴포넌트
개요
Cals WebComponent는 외부에서 빌드된 React 또는 Vue 컴포넌트 번들을 Shadow DOM 내에 격리된 환경으로 로드·마운트합니다. 번들의 export를 분석하여 프레임워크를 자동 감지하므로, 호스트 앱에서 프레임워크 타입을 명시할 필요가 없습니다.
주요 특징
- ✅ 프레임워크 자동 감지: 번들이
createApp을 export하면 Vue, 아니면 React로 자동 판별 - ✅ Shadow DOM 격리: 외부 컴포넌트 스타일이 호스트 페이지에 영향 없음
- ✅ 동적 Props 동기화:
componentProps변경 시 재마운트 없이 re-render - ✅ 자동 스타일 주입: 외부 번들의
config.js+assets/style.css자동 로드 - ✅ depth 지원: 번들이
depth를 export하면 해당 값만큼 상위 디렉터리를 baseURL로 사용 (Extension-Plugin 호환) - ✅ 라이프사이클 관리: 마운트/언마운트 시 React root / Vue app 자동 정리
- ✅ children 지원: Shadow DOM 컨테이너 바로 아래에 추가 UI 렌더링 가능
기본 사용
src로 외부 컴포넌트 번들 URL을 지정합니다. 프레임워크(React/Vue)는 자동 감지됩니다.
Preview
WebComponent 렌더 영역 (외부 번들 필요)
동적 Props 전달
componentProps가 변경되면 컴포넌트를 재마운트하지 않고 React는 re-render, Vue는 reactive ref 동기화로 처리합니다.
Code
const [theme, setTheme] = useState("light")
<WebComponent
src="https://cdn.example.com/extension/Dashboard.js"
componentProps={{ theme, screenId: "SCR-001", token: accessToken }}
height={400}
/>오버레이 (children)
children을 전달하면 Shadow DOM 컨테이너 바로 아래에 오버레이로 렌더링됩니다.
Code
<WebComponent src="https://cdn.example.com/extension/Widget.js" height={250}>
<div className="absolute inset-0 flex items-center justify-center bg-black/20">
<span className="text-white">로딩 중...</span>
</div>
</WebComponent>useWebComponent Hook
WebComponent 컴포넌트 대신 훅을 직접 사용할 수도 있습니다.
import { useWebComponent } from "@vortex/ui-cals";
import { useRef } from "react";
function CustomContainer() {
const containerRef = useRef<HTMLDivElement>(null);
useWebComponent(containerRef, {
src: "/extension/MyWidget.js",
componentProps: { theme: "dark" },
shadowMode: "open",
});
return <div ref={containerRef} style={{ height: 400 }} />;
}API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | (필수) | 외부 컴포넌트 번들의 전체 URL |
componentProps | Record<string, unknown> | {} | 컴포넌트에 전달할 props 객체 |
type | "auto" | "react" | "vue" | "custom" | "auto" | 마운트 방식 (auto: 번들 export 분석으로 자동 판별) |
shadowMode | ShadowRootMode | "open" | Shadow DOM 모드 (“open” / “closed”) |
customMount | CustomMountFunctions | - | type="custom" 시 사용할 mount 함수 |
height | string | number | - | Shadow DOM 컨테이너 높이 |
className | string | - | 외부 컨테이너 CSS 클래스 |
style | CSSProperties | - | 외부 컨테이너 인라인 스타일 |
children | ReactNode | - | 오버레이 등 추가 렌더 콘텐츠 |
useWebComponent Options
| Option | Type | Default | Description |
|---|---|---|---|
src | string | (필수) | 외부 번들 URL |
componentProps | Record<string, unknown> | {} | 컴포넌트에 전달할 props |
type | "auto" | "react" | "vue" | "custom" | "auto" | 마운트 방식 (auto: 번들 export 분석으로 자동 판별) |
shadowMode | ShadowRootMode | "open" | Shadow DOM 모드 |
customMount | CustomMountFunctions | - | type="custom" 시 사용할 mount 함수 |
외부 번들 규약
React 번들
React 컴포넌트를 default export합니다:
// 번들 파일 (예: MyWidget.js)
export default function MyWidget(props: Record<string, unknown>) {
return <div>...</div>;
}Vue 번들 (Extension-Plugin)
@cals/extension-plugin의 calsExPlugin()으로 빌드된 번들은 자동으로 호환됩니다.
번들은 createApp, ref를 named export하며, CalsWrapper가 내장되어 있어
componentProps로 전달한 CALS 컨텍스트(CalsSdk, emitter, token 등)가 Vue provide/inject로 자동 배포됩니다.
// Extension-Plugin이 자동 생성하는 번들 구조
export { createApp, ref } from 'vue'
export default h(CalsWrapper, ...) // CalsWrapper가 props를 provide호스트에서 전달해야 하는 componentProps:
| Prop | Type | Description |
|---|---|---|
calsSdk | CalsSdk | 호스트 앱 API (Alert, Navigate 등) |
emitter | Emitter | mitt 기반 이벤트 버스 |
token | string | 인증 토큰 |
screenId | string | 화면 ID |
componentId | string | 컴포넌트 ID |
theme | string | 테마 (light/dark) |
lang | string | 언어 (ko/en) |
createApp이 export에 존재하면 자동으로 Vue로 감지되며, ref도 있으면
Extension-Plugin 패턴으로 reactive props를 전달합니다.
Props 변경 시 Vue 반응성으로 자동 갱신되므로 재마운트가 필요 없습니다.
Vue 번들 (직접 빌드)
Extension-Plugin 없이 직접 빌드한 Vue 번들도 지원됩니다:
// 번들 파일 (예: MyWidget.js)
import MyComponent from "./MyComponent.vue";
export { createApp, ref } from "vue";
export default MyComponent;선택적으로 동일 baseURL에 다음 파일을 배치할 수 있습니다:
| 파일 | 역할 |
|---|---|
config.js | globalCss 배열로 추가 CSS 문자열 주입 |
assets/style.css | Shadow DOM 내에 주입할 스타일시트 |
depth (baseURL 보정)
번들이 depth를 named export하면, 해당 값만큼 상위 디렉터리를 baseURL로 사용합니다.
예를 들어 depth = 1이고 src = "https://cdn.example.com/plugins/v1/MyWidget.js"이면,
baseURL은 https://cdn.example.com/plugins가 됩니다 (기본값 0이면 https://cdn.example.com/plugins/v1).
이는 Extension-Plugin의 calsExPlugin() 빌드 구조와 호환됩니다.
// 번들 파일에서 depth export (선택)
export const depth = 1; // config.js가 1단계 상위 디렉터리에 위치접근성
권장 사항
- ✅ Shadow DOM 내부의 포커스 가능한 요소는 문서 순서에 자연스럽게 통합됩니다.
- ✅ 외부 컴포넌트 내에서 ARIA 속성을 적절히 사용하도록 번들 개발 시 고려합니다.
- ✅ 로딩 상태를
children오버레이로 제공할 때aria-live="polite"를 고려합니다.