---
title: "CDN / 스크립트 태그"
description: "번들러 없이 모든 웹사이트에 ThunderPhone 음성 위젯을 추가합니다"
---

CDN 빌드는 React를 내부적으로 번들링하므로 정적 사이트, WordPress, Webflow 또는 HTML을 추가할 수 있는 모든 페이지에서 위젯을 사용할 수 있습니다. npm, 번들러, 프레임워크가 필요하지 않습니다.

## CDN URL

<CodeGroup>
```html Latest (recommended)
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
```

```html Pinned version
<!-- Each stable release also publishes an immutable /widget/vX.Y.Z/ path
     matching the @thunderphone/widget npm version (v1.1.2 at the time of
     writing). If you pin, you own updating the URL for new releases. -->
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/v1.1.2/style.css" />
<script src="https://cdn.thunderphone.com/widget/v1.1.2/widget.js"></script>
```
</CodeGroup>

<Tip>
  `latest` URL은 대시보드의 임베드 스니펫 생성기가 만드는 URL입니다. 5분간 캐시되며 새 안정 릴리스를 자동으로 반영합니다. 고정된 `/widget/vX.Y.Z/` 경로는 변경할 수 없으며 장기간 캐시됩니다. 고정하는 경우 개발에 사용하는 `@thunderphone/widget` npm 릴리스와 버전을 일치시키십시오.
</Tip>

---

## 기본 사용법

```html
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
</head>
<body>

  <div id="thunderphone"></div>

  <script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
  <script>
    ThunderPhone.mount({
      element: '#thunderphone',
      publishableKey: 'pk_live_your_publishable_key',
    })
  </script>

</body>
</html>
```

위젯은 대상 요소에 마운트되며 **뷰포트 모서리에 고정 위치 바**로 렌더링됩니다(기본값은 `bottom-right`이며 `position` 옵션으로 제어됨). 마운트 요소는 React 루트일 뿐이며, 페이지 내 위치는 위젯이 표시되는 위치에 영향을 주지 않습니다.

---

## 마운트 옵션

`ThunderPhone.mount()`는 React 컴포넌트와 동일한 옵션에 `element` 속성을 추가하여 지원합니다.

| 옵션 | 유형 | 필수 | 기본값 | 설명 |
|--------|------|----------|---------|-------------|
| `element` | `string \| HTMLElement` | 예 | -- | CSS 선택자(예: `'#thunderphone'`) 또는 DOM 요소 참조입니다. |
| `publishableKey` | `string` | 예 | -- | 게시 가능 API 키(`pk_live_...`)입니다. 에이전트는 키의 위젯 구성에서 자동으로 확인됩니다. |
| `theme` | `'light' \| 'dark'` | 아니요 | `'light'` | 색상 구성표입니다. |
| `primaryColor` | `string` | 아니요 | `'#000000'` (light) / `'#ffffff'` (dark) | 강조 색상으로 사용되는 CSS 색상 문자열입니다. |
| `title` | `string` | 아니요 | `'Voice assistant'` | 위젯 바에 표시되는 텍스트입니다. |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | 아니요 | `'bottom-right'` | 고정된 뷰포트 위치입니다. |
| `apiBase` | `string` | 아니요 | `'https://api.thunderphone.com/v1'` | API 기본 URL을 재정의합니다. |
| `language` | `string` | 아니요 | -- | 세션별 언어 재정의입니다. `en`, `es`, `fr-FR` 등의 언어 코드 또는 로캘을 지정합니다. 설정하지 않으면 에이전트에 구성된 언어가 적용됩니다. 키의 [브라우저 재정의](/ko/guides/embed-web-widget)에서 `language`가 필요합니다. |
| `voice` | `string` | 아니요 | -- | 세션별 음성 재정의입니다. `maria`와 같은 음성 이름을 지정합니다. 설정하지 않으면 에이전트에 구성된 음성이 적용됩니다. 키의 [브라우저 재정의](/ko/guides/embed-web-widget)에서 `voice`가 필요합니다. |
| `context` | `string` | 아니요 | -- | 에이전트에 전달되는 세션별 사실 기반 페이지 또는 사이트 컨텍스트입니다. 서버 측에서 12,000자로 잘립니다. 키의 [브라우저 재정의](/ko/guides/embed-web-widget)에서 `context`가 필요합니다. |
| `onConnect` | `() => void` | 아니요 | -- | 음성 세션이 연결되면 호출됩니다. |
| `onDisconnect` | `() => void` | 아니요 | -- | 세션이 종료되면 호출됩니다. |
| `onError` | `(error) => void` | 아니요 | -- | 오류 발생 시 호출됩니다. 오류에는 `error`(코드) 및 `message` 필드가 있습니다. |
| `ringtone` | `boolean \| string` | 아니요 | `false` | 연결하는 동안 벨소리를 재생합니다. 기본 벨소리는 `true`를 사용하고, 사용자 지정 오디오는 URL 문자열을 사용합니다. |

---

## 정리

`ThunderPhone.mount()`는 정리 메서드를 포함하는 위젯 인스턴스를 반환합니다.

```html
<script>
  var widget = ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
  })

  // Later, when you want to remove the widget:
  widget.unmount()

  // Or equivalently:
  widget.destroy()
</script>
```

`unmount()`와 `destroy()`는 동일하게 작동합니다. 활성 음성 세션의 연결을 해제하고 DOM에서 위젯을 제거합니다. 코드에서 더 자연스럽게 읽히는 메서드를 사용합니다.

<Note>
  단일 페이지 애플리케이션에서 다른 페이지로 이동하거나 포함 요소가 제거될 때는 항상 위젯을 정리합니다. 이렇게 하면 활성 음성 세션의 연결이 올바르게 해제됩니다.
</Note>

---

## 예시

### 사용자 지정 색상의 다크 테마

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    theme: 'dark',
    primaryColor: '#8b5cf6',
    title: 'Talk to our AI',
  })
</script>
```

### 사용자 지정 위치

```html
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    position: 'bottom-left',
  })
</script>
```

### 이벤트 콜백 사용

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: function () {
      console.log('Call started')
    },
    onDisconnect: function () {
      console.log('Call ended')
    },
    onError: function (error) {
      console.error('Widget error:', error.error, error.message)
    },
  })
</script>
```

### 벨소리 사용

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })
</script>
```

### DOM 요소 참조 사용

```html
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  var container = document.getElementById('thunderphone')

  ThunderPhone.mount({
    element: container,
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

### WordPress / CMS 통합

사용자 지정 HTML 블록 또는 테마의 푸터에 다음을 추가합니다.

```html
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>

<div id="thunderphone-widget"></div>
<script>
  ThunderPhone.mount({
    element: '#thunderphone-widget',
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

<Tip>
  `<div>`는 어디에나 배치할 수 있으며 마운트 지점일 뿐입니다. 위젯 자체는 뷰포트 모서리에 고정 오버레이로 렌더링되며(`position` 옵션으로 설정), 주변 콘텐츠의 흐름에 영향을 받지 않습니다.
</Tip>

---

## 문제 해결

<AccordionGroup>
  <Accordion title="위젯이 표시되지 않음">
    CSS 및 JS 파일이 모두 로드되었는지 확인합니다. 브라우저 콘솔에서 네트워크 오류를 확인합니다. `ThunderPhone.mount()`를 호출하기 전에 대상 요소가 DOM에 있는지 확인합니다.
  </Accordion>

  <Accordion title="ThunderPhone이 정의되지 않음">
    스크립트가 아직 로드되지 않았습니다. `widget.js`의 `<script>` 태그가 마운트 호출 전에 있는지 확인하거나, 마운트 호출을 `DOMContentLoaded` 리스너로 감쌉니다.
  </Accordion>

  <Accordion title="도메인이 허용되지 않음 오류">
    [app.thunderphone.com](https://app.thunderphone.com)의 **웹 위젯** 페이지에서 위젯의 허용 도메인에 도메인을 추가합니다. 프로덕션에서는 `localhost`가 자동으로 허용되지 않으므로, 로컬에서 테스트하려면 목록에 추가합니다.
  </Accordion>
</AccordionGroup>

---

## 다음 단계

<CardGroup cols={2}>
  <Card title="스타일 지정" icon="palette" href="/ko/widget/styling">
    CSS 사용자 지정 속성으로 위젯 모양을 사용자 지정합니다.
  </Card>
  <Card title="React 컴포넌트" icon="react" href="/ko/widget/react">
    React를 사용하시나요? 컴포넌트 통합이 더 간단합니다.
  </Card>
</CardGroup>
