Web Components는 프레임워크에 종속되지 않는 표준 웹 기술입니다. Vue 컴포넌트를 Web Component로 변환하면 React, Angular, 바닐라 JS 등 어디서든 사용할 수 있습니다.

면접에서 "프레임워크에 독립적인 UI 컴포넌트를 만들려면 어떻게 하나요?"라고 물으면 Web Components를 언급할 수 있어야 합니다.


Vue 컴포넌트를 Web Component로 변환

TYPESCRIPT
// defineCustomElement — Vue 3.2+
import { defineCustomElement } from 'vue'

// 일반 Vue SFC를 Custom Element로 변환
import MyButton from './MyButton.ce.vue'  // .ce.vue 확장자 권장

const MyButtonElement = defineCustomElement(MyButton)

// 브라우저에 등록
customElements.define('my-button', MyButtonElement)
VUE
<!-- MyButton.ce.vue -->
<script setup lang="ts">
const props = defineProps<{
  label: string
  variant?: 'primary' | 'secondary'
}>()

const emit = defineEmits<{
  click: [event: MouseEvent]
}>()
</script>

<template>
  <button :class="variant" @click="emit('click', $event)">
    {{ label }}
  </button>
</template>

<!-- Custom Element에서는 scoped가 아닌 일반 style 사용 -->
<!-- Shadow DOM이 스타일을 격리함 -->
<style>
button {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.primary { background: #42b883; color: white; }
.secondary { background: #e0e0e0; color: #333; }
</style>
HTML
<!-- 어디서든 사용 가능 -->
<my-button label="클릭" variant="primary"></my-button>

<script>
document.querySelector('my-button').addEventListener('click', (e) => {
  console.log('클릭됨!')
})
</script>

외부 Web Component를 Vue에서 사용

TYPESCRIPT
// vite.config.ts — 커스텀 엘리먼트 인식 설정
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // 특정 태그를 커스텀 엘리먼트로 인식
          isCustomElement: (tag) => tag.startsWith('ion-') || tag.startsWith('sl-')
        }
      }
    })
  ]
})
VUE
<template>
  <!-- Shoelace Web Components 예시 -->
  <sl-button variant="primary" @sl-click="handleClick">
    버튼
  </sl-button>

  <sl-dialog :open="isOpen" @sl-after-hide="isOpen = false">
    <span slot="label">다이얼로그 제목</span>
    <p>내용입니다.</p>
  </sl-dialog>
</template>

Props와 Events 매핑

PLAINTEXT
Vue Props → HTML Attributes / DOM Properties
Vue Emit  → Custom Events (CustomEvent)

주의사항:
- HTML 속성은 문자열만 가능 → 복잡한 데이터는 DOM property로
- 이벤트 이름은 kebab-case
- v-model은 직접 구현 필요

Shadow DOM과 스타일

Custom Element는 기본적으로 Shadow DOM을 사용하여 스타일이 격리됩니다.

VUE
<!-- 스타일이 Shadow DOM 내부에 캡슐화됨 -->
<style>
/* 이 스타일은 Custom Element 외부에 영향을 주지 않음 */
button { color: red; }
</style>

Shadow DOM을 비활성화하려면:

TYPESCRIPT
const MyElement = defineCustomElement(MyComponent, {
  shadowRoot: false  // Shadow DOM 비활성화
})

면접 팁

  • Web Components는 표준 기술 이므로 프레임워크 업데이트에 영향받지 않습니다
  • Vue의 defineCustomElement는 SFC의 모든 기능(props, emit, slots, 스타일)을 지원합니다
  • Shadow DOM의 스타일 격리가 장점이자 단점이 될 수 있다는 것을 이해하고 있어야 합니다

요약

Vue 컴포넌트를 defineCustomElement로 Web Component로 변환하면 프레임워크에 독립적으로 사용할 수 있습니다. 외부 Web Component는 isCustomElement 설정으로 Vue에서 인식하게 하면 됩니다. Shadow DOM이 스타일을 격리하므로 CSS 충돌 걱정 없이 사용할 수 있습니다.

댓글 로딩 중...