퀵스타트
공개 wippyai/app 저장소에서 가져온 두 개의 엔드투엔드 예제 — 마이크로 프론트엔드 앱(Vue)과 웹 컴포넌트(Vue)입니다. 각각 최소한의 파일, 산출물을 백엔드에 등록하는 방법, 빌드하는 방법을 보여줍니다. 완전하고 실행 가능한 소스는 저장소 링크를, 각 옵션의 자세한 내용은 심화 문서를 따라가세요.
사전 요구 사항: wippy/views와 wippy/facade 모듈이 연결된 Wippy 백엔드, Node.js 22 이상, Vite 7, 그리고 대상 웹 호스트에 맞게 선택된 현행 일관 @wippy-fe/* 패키지 계열. 이 툴체인 요구 사항은 선택된 웹 호스트 패키지에서 나오므로, 그 패키지가 바뀌면 다시 확인하세요. 대상 웹 호스트의 import-map.json을 가져와, 사용하지 않는 것을 포함해 나열된 모든 키를 외부 모듈로 지정하고, 정확한 스펙파이어가 없을 때에만 임포트한 것을 번들에 포함하세요. 툴체인은 빌드 시스템을 참고하세요.
예제 1 — 마이크로 프론트엔드 앱 (Vue)
웹 호스트가 선택된 페이지 엔진(기본은 iframe, 또는 Web Fragment)으로 렌더링하는 완전한 Vue 3 SPA입니다. 저장소: frontend/applications/main.
package.json — wippy 블록이 이를 페이지로 선언하고 호스트가 주입할 CSS를 지정합니다:
{
"name": "@example/admin",
"specification": "wippy-component-1.0",
"wippy": {
"type": "page",
"title": "Admin",
"icon": "tabler:layout-dashboard",
"path": "dist/app.html",
"proxy": {
"enabled": true,
"injections": {
"css": { "themeConfig": true, "iframe": true, "primevue": true }
}
}
}
}
src/app.ts — 호스트 서비스를 해석하고, 마운트하고, 필수인 양방향 라우트 동기화를 연결합니다:
import { config } from '@wippy-fe/proxy' // 동기 게터 — 얻는 데 await가 필요 없습니다
import { createApp } from 'vue'
import { createAppRouter } from '@wippy-fe/router'
import App from './app/app.vue'
import { routes } from './router'
export function createMainApp() {
const app = createApp(App)
const initialPath = config.context?.route ?? '/'
const router = createAppRouter(routes, { initialPath })
app.use(router)
app.mount('#app')
return { app, router }
}
모듈의 _index.yaml에 등록하세요(이는 운영자/배포 정책입니다 — 마이크로 프론트엔드 앱 (view.page) 참조):
- name: admin
kind: registry.entry
meta:
type: view.page
name: admin
announced: true # 호스트 내비게이션 사이드바에 표시
url: /app
base_path: app/admin
entry_point: app.html
mountRoute: /admin/:part(.*)*
모듈의 Make 타깃을 호출해 서빙 디렉터리로 빌드한 뒤, url + base_path가 가리키는
곳에서 출력을 서빙하세요. 호스트가 이를 /admin에 렌더링합니다.
Makefile 레시피는
npm run build -- --outDir <abs-or-relative> --emptyOutDir를 사용합니다. make.ps1은
Windows용으로 동일한 타깃을 구현하고, make.bat은 make.ps1을 호출하기만 합니다.
전체 안내: 마이크로 프론트엔드 앱.
예제 2 — 웹 컴포넌트 (Vue)
호스트가 페이지 DOM(Shadow DOM)에 마운트하는 커스텀 엘리먼트로, 어떤 페이지나 채팅 아티팩트에서도 임베드할 수 있습니다. 저장소: frontend/web-components/reaction-bar.
package.json — wippy 블록이 태그, props(HTML 어트리뷰트), 이벤트를 선언합니다:
{
"name": "@example/reaction-bar",
"specification": "wippy-component-1.0",
"wippy": {
"tagName": "example-reaction-bar",
"type": "widget",
"props": {
"type": "object",
"properties": {
"reactions": { "type": "array", "items": { "type": "string" }, "default": ["👍", "👎", "❤️"] },
"allow-multiple": { "type": "boolean", "default": false }
}
},
"events": {
"type": "object",
"properties": { "reaction": { "type": "object", "description": "Fired when a reaction is toggled" } }
}
}
}
src/index.ts — Vue 컴포넌트를 WippyVueElement로 감싸 등록합니다. define(import.meta.url, …)는 호스트가 덧붙이는 ?declare-tag= 쿼리를 읽으며, 그래서 반드시 import.meta.url을 사용해야 합니다:
import { WippyVueElement, define } from '@wippy-fe/webcomponent-vue'
import { PrimeVuePlugin } from '@wippy-fe/theme/primevue-plugin'
import ReactionBar from './app/reaction-bar.vue'
import stylesText from './styles.css?inline'
import pkg from '../package.json'
class ReactionBarElement extends WippyVueElement {
static get wippyConfig() {
return {
propsSchema: pkg.wippy.props,
hostCssKeys: ['themeConfigUrl', 'primeVueCssUrl'] as const, // 호스트 테마 + PrimeVue를 섀도우 루트로 가져옵니다
inlineCss: stylesText,
}
}
static get vueConfig() {
return { rootComponent: ReactionBar, plugins: [PrimeVuePlugin] }
}
}
export async function webComponent() {
return ReactionBarElement
}
define(import.meta.url, ReactionBarElement)
src/app/reaction-bar.vue — @wippy-fe/webcomponent-vue 컴포저블로 props를 읽고 이벤트를 방출합니다:
<script setup lang="ts">
import Button from 'primevue/button'
import { ref, computed } from 'vue'
import { useComponentProps, useComponentEvents } from '../constants'
const props = useComponentProps()
const emit = useComponentEvents()
const active = ref(new Set<string>())
const reactions = computed(() => props.value.reactions ?? [])
function toggle(emoji: string) {
active.value.has(emoji) ? active.value.delete(emoji) : active.value.add(emoji)
active.value = new Set(active.value)
emit('reaction', { emoji, count: active.value.has(emoji) ? 1 : 0, active: active.value.has(emoji) })
}
</script>
<template>
<Button
v-for="emoji in reactions"
:key="emoji"
:label="emoji"
:aria-label="`Toggle ${emoji} reaction`"
:aria-pressed="active.has(emoji)"
text
@click="toggle(emoji)"
/>
</template>
(useComponentProps / useComponentEvents는 src/constants.ts에 정의된 얇은 useProps() / useEvents() 래퍼입니다.)
view.component로 등록하세요(자동 로드에는 세 관문이 모두 필요합니다 — 웹 컴포넌트 (view.component) 참조):
- name: reaction-bar
kind: registry.entry
meta:
type: view.component
name: reaction-bar
tag_name: example-reaction-bar
announced: true
auto_register: true
url: /app/wc/reaction-bar
entry_point: index.js
빌드하고 나면 어떤 페이지(또는 채팅 아티팩트)에서든 태그를 사용할 수 있습니다:
<example-reaction-bar reactions='["👍","🎉"]'></example-reaction-bar>
전체 안내: 웹 컴포넌트.
더 살펴보기
app 저장소는 frontend/web-components/ 아래에 실행 가능한 여러 웹 컴포넌트를 제공합니다:
| 컴포넌트 | 보여주는 것 |
|---|---|
reaction-bar |
Props + 이벤트 방출 |
counter-persist |
@wippy-fe/pinia-persist로 리로드에도 살아남는 상태 |
chart-circle |
Shadow DOM에 서드파티 라이브러리(Chart.js) 번들링 |
mermaid |
자식 콘텐츠(<template data-type="…">) + 지연 폴백 번들 |
markdown |
markdown-it + sanitize-html |
websocket-log |
on(...) 토픽 구독을 통한 실시간 데이터 |
model-gallery |
프록시를 통한 인증된 API 호출 + Shadow DOM 안의 PrimeVue |
두 산출물의 테마 적용은 테마 적용 → 테마 적용: 마이크로 프론트엔드 앱 / 테마 적용: 웹 컴포넌트를 읽으세요. 전체 호스트 없이 로컬에서 실행하려면 호스트리스 모드를 참고하세요.