페이지에서 위젯을 구동하고 모든 대화를 탄력적으로 유지합니다 — 자동 재연결, 메시지 재시도, 오프라인 감지, 시간 초과 및 친절한 오류 처리.
위젯 스크립트가 로드되면, Companin은 window.CompaninWidgetHost에 호스트 브리지를 등록합니다. 이는 귀하의 페이지에 위젯을 열고 닫고, 메시지를 보내고, 상태를 읽고, 생명 주기 이벤트에 구독할 수 있는 작고 의존성 없는 API를 제공합니다 — 귀하의 코드를 위젯의 내부와 결합하지 않고도 가능합니다.
브리지는 또한 위젯 위에 신뢰성 계층을 추가합니다: 메시지는 오프라인 상태에서 대기열에 추가되며, 연결이 복원되면 자동으로 재시도되고, 응답이 도착하지 않으면 시간 초과되며, 일시적인 오류 후 위젯이 자동으로 재연결됩니다. 모든 상태 변경은 DOM 이벤트로 표시되어 귀하의 UI에서 반응할 수 있습니다.
위젯이 로드된 후 window.CompaninWidgetHost에서 이러한 메서드를 호출합니다. 위젯이 아직 준비되지 않은 경우 안전한 no-op이므로 모든 호출을 보호할 필요가 없습니다.
open() / close() / toggle() — 위젯 패널을 표시, 숨기거나 뒤집습니다.sendText(text) — 방문자로서 일반 텍스트 메시지를 보냅니다.sendPayload(payload) — 구조화된 메시지 또는 명령 객체를 보냅니다.sendSafe(payload) — 신뢰성 계층을 통해 전송합니다 — 오프라인 상태에서 대기열에 추가되고 모든 가로채기를 통과합니다.sendWithTimeout(payload, ms) — ms(기본값 10000) 내에 응답이 도착하지 않으면 시간 초과 이벤트를 전송하고 방출합니다.intercept(fn) — 나가는 메시지를 재작성하거나 취소할 수 있는 함수를 등록합니다; 구독 취소 함수를 반환합니다.getState() — 현재 호스트 상태를 읽습니다: 열기 상태, 마지막으로 전송 및 수신된 메시지, 온라인 플래그 및 명령 기록.getIsOnline() — 호스트가 현재 연결을 온라인으로 간주하는지 여부.drainRetryQueue() — 오프라인 상태에서 대기열에 추가된 모든 메시지를 수동으로 플러시합니다.cleanup() — 모든 리스너의 구독을 취소합니다 — 위젯을 제거하기 전에 호출합니다.<script> window.addEventListener('load', function () { var host = window.CompaninWidgetHost; if (!host) return; host.open(); host.sendText('Hi! I have a question about pricing.'); console.log(host.getState()); });</script>위젯이 로그인한 사용자를 인식하도록 하여 대화가 개인화되고 장치 간에 복원됩니다. 귀하의 서버는 위젯이 Companin에 전달하는 단기 토큰을 서명합니다; Companin은 이를 검증하고 세션을 해당 사용자와 연결합니다.
1. 서명 비밀을 가져옵니다. 대시보드에서 설치 → 로그인한 사용자 → 비밀 생성으로 이동한 후, 이를 귀하의 서버 환경에 복사합니다. 브라우저 코드에 노출하지 마십시오.
2. 서버에서 토큰을 서명합니다 — 사용자의 id(sub), 이메일 및 이름을 포함하는 단기 HS256 JWT:
const jwt = require('jsonwebtoken'); // npm i jsonwebtokenfunction signUserToken(user) { return jwt.sign( { sub: String(user.id), email: user.email, name: user.name }, process.env.COMPANIN_EMBED_SECRET, { algorithm: 'HS256', expiresIn: '5m' } );}3. 위젯에 토큰을 전달합니다. 서버 렌더링 페이지에서 스크립트 태그에 data-user-token으로 추가합니다; 단일 페이지 앱에서는 사용자가 로그인한 후 identify()를 호출합니다:
<!-- Option A: server-rendered page — put the signed token on the script tag --><script src="https://YOUR_WIDGET_HOST/widget.js" data-widget-key="wgt_your_key" data-user-token="SERVER_SIGNED_JWT"></script><!-- Option B: after login — fetch a fresh token and call identify() --><script> fetch('/api/widget-user-token') .then(function (r) { return r.json(); }) .then(function (data) { if (data.token) window.CompaninWidget.identify({ token: data.token }); });</script>잘못되거나 만료된 토큰은 무시됩니다 — 위젯은 단순히 익명 상태를 유지하므로 항상 식별을 시도하는 것이 안전합니다.
전달이 중요한 경우 sendSafe 대신 sendText를 사용합니다. 호스트는 브라우저의 온라인 및 오프라인 이벤트를 감시합니다; 오프라인 상태에서 메시지는 재시도 대기열에 추가되고 companin:widget:offline 이벤트가 발생합니다. 연결이 복원되면 대기열이 자동으로 순서대로 비워지고 companin:widget:online 이벤트가 발생합니다.
window.CompaninWidgetHost.sendSafe('Track my order #1234');window.addEventListener('companin:widget:offline', function () { showBanner('You are offline — your message will send automatically.');});window.addEventListener('companin:widget:online', function () { showBanner('Back online.');});window.addEventListener('companin:widget:retryDrained', function (e) { showBanner(e.detail.count + ' queued message(s) sent.');});응답이 도착하지 않는 경우를 대비하여 sendWithTimeout으로 전송을 감싸십시오. 시간 초과(기본값 10초) 내에 응답이 수신되지 않으면 companin:widget:timeout 이벤트가 발생하여 사용자가 기다리지 않고 친절한 재시도 프롬프트를 표시할 수 있습니다.
window.CompaninWidgetHost.sendWithTimeout('Are you there?', 8000);window.addEventListener('companin:widget:timeout', function () { showRetryPrompt('That took longer than expected. Try again?');});위젯이 오류를 보고하면, 호스트는 증가하는 백오프(1.5초, 3초, 4.5초)로 최대 세 번 재연결을 시도하며, 각 시도마다 companin:widget:reconnecting 이벤트를 발생시킵니다. 성공적인 응답은 카운터를 재설정합니다; 모든 시도가 실패하면 companin:widget:reconnectFailed가 발생하여 우아하게 대체할 수 있습니다.
window.addEventListener('companin:widget:reconnecting', function (e) { console.log('Reconnecting — attempt', e.detail.attempt, 'in', e.detail.delay, 'ms');});window.addEventListener('companin:widget:reconnectFailed', function () { showBanner('We could not reconnect. Please refresh the page.');});모든 나가는 메시지를 검사, 재작성 또는 취소하기 위해 intercept(fn)으로 인터셉터를 등록합니다. 변경된 페이로드를 반환하여 변경하거나, false를 반환하여 전송을 취소하거나, 아무것도 반환하지 않아 변경 없이 통과하도록 합니다. intercept는 구독 취소 함수를 반환합니다.
const stop = window.CompaninWidgetHost.intercept(function (payload) { if (typeof payload === 'string') { if (!payload.trim()) return false; // cancel the send return payload.replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, '[email]'); } return payload;});// Later, to remove the interceptor:stop();호스트는 모든 위젯 생명 주기 변경을 DOM CustomEvent로 window에 다시 방출하므로, 위젯에 대한 참조를 보유하지 않고도 반응할 수 있습니다. 관련 데이터는 event.detail에 있습니다.
companin:widget:open / close — 위젯 패널이 열리거나 닫혔습니다.companin:widget:message / response — 방문자가 메시지를 보냈거나 에이전트가 응답했습니다.companin:widget:authFailure — 백엔드와의 인증이 실패했습니다.companin:widget:error — 위젯이 오류를 보고했습니다.companin:widget:offline / online — 브라우저가 연결을 잃거나 다시 연결했습니다.companin:widget:queued / retryDrained — 오프라인 상태에서 메시지가 대기열에 추가되었거나 대기열이 비워졌습니다.companin:widget:reconnecting / reconnectFailed — 자동 재연결 시도가 시작되었거나 모든 시도가 소진되었습니다.companin:widget:timeout — 메시지가 시간 초과 내에 응답을 받지 못했습니다.window.addEventListener('companin:widget:message', function (e) { console.log('Visitor sent:', e.detail.message);});window.addEventListener('companin:widget:response', function (e) { console.log('Agent replied:', e.detail.response);});window.addEventListener('companin:widget:authFailure', function (e) { console.warn('Widget auth failed:', e.detail.error);});디커플된 코드는 API를 직접 호출하는 대신 companin:widget:command 이벤트를 발생시켜 위젯을 구동할 수 있습니다. 메시지를 전달하기 위해 문자열을 보내거나, { action: 'open' } 또는 { text: 'Hello' }와 같은 세부 객체를 보냅니다. 이는 위젯을 가져오지 않아야 하는 분석 태그, GTM 또는 기타 스크립트에 유용합니다.
// Open the widget from anywhere — no widget reference needed.window.dispatchEvent(new CustomEvent('companin:widget:command', { detail: { action: 'open' }}));// Or send a message.window.dispatchEvent(new CustomEvent('companin:widget:command', { detail: { text: 'I need help with billing' }}));window.__COMPANIN_WIDGET_WEBHOOK_URL을 수집기 엔드포인트로 설정하면, 호스트는 이를 JSON으로 navigator.sendBeacon을 통해 열기, 닫기, 메시지 및 응답 이벤트를 전달합니다(keepalive fetch로 대체됨). 이는 서버 측 웹훅에 대한 경량 클라이언트 측 보완으로 — 제1자 분석에 유용합니다.
// Point the host at your collector before the widget loads.window.__COMPANIN_WIDGET_WEBHOOK_URL = 'https://example.com/collect/widget';