사이트 위젯

위젯은 봇의 채팅을 사이트에 그대로 올린 것입니다. 페이지 모서리의 둥근 버튼을 누르면 대화가 열립니다. 방문자에게는 연락 수단이 하나 늘어난 것이고, 봇에게는 평범한 채널입니다. 메신저와 똑같은 반응, 똑같은 상담원 대화를 씁니다. 멀티채널 개요는 채널 문서를 보세요.

설치는 사이트 템플릿에 코드 한 조각을 붙여 넣는 일이 전부입니다. 나머지는 모두 관리 화면에서 설정하며, 사이트를 다시 건드리지 않아도 반영됩니다.

설치 코드를 얻는 곳

관리 화면에서 위젯사이트에 설치 블록 → 삽입할 코드 필드를 엽니다. 복사 버튼을 누르면 조각 전체가 클립보드에 담깁니다.

코드는 다음과 같으며, 말줄임표 자리에는 여러분의 키가 들어갑니다.

<script>
  window.mybot = { key: "eu-1a2b3c4d-..." };
</script>
<script async src="https://getmybot.dev/loader.js"></script>

코드 대신 "이 봇에는 아직 설치 키가 발급되지 않았습니다"라는 문구가 보이면 사이트 위젯 채널이 연결되지 않은 상태입니다. 지원팀에 문의하세요. 키는 채널을 연결할 때 발급됩니다.

코드 아래에는 위젯이 어느 호스트에 접속하는지 안내가 표시됩니다. 사이트에 CSP(Content Security Policy)를 적용했다면 해당 호스트의 스크립트 로드와 네트워크 요청을 허용해야 합니다. 그렇지 않으면 브라우저가 조용히 위젯을 차단합니다.

코드를 넣을 위치

이 조각은 위젯이 보여야 할 모든 페이지의 HTML에 들어갑니다. 실무적으로는 공통 템플릿에 한 번만 넣습니다. 푸터, CMS의 "</body> 앞 코드" 필드, 태그 관리자 컨테이너 등입니다.

규칙은 간단합니다.

  • 가장 좋은 자리는 닫는 </body> 바로 앞입니다. <head>에서도 동작하지만, 그러면 브라우저가 본문보다 위젯에 먼저 시간을 씁니다.
  • 두 태그의 순서가 중요합니다. 앞의 것이 키를 지정하고, 뒤의 것이 위젯을 불러옵니다. 순서를 바꾸거나 페이지 곳곳에 나눠 두지 마세요.
  • 두 번째 태그에는 async가 있어 렌더링을 막지 않습니다. 이 속성을 지우지 마세요.
  • 페이지당 한 조각입니다. 두 벌을 넣으면 위젯을 두 번 시작하려 합니다.

이후에는 로더가 알아서 해당 봇을 담당하는 데이터센터를 판별하고 거기서 위젯 본체를 내려받습니다. 이를 위해 따로 할 일은 없습니다.

설치 키는 공개 정보입니다

첫 번째 태그의 키는 봇의 공개 식별자이며 비밀번호가 아닙니다. 페이지 소스에 그대로 들어 있어 방문자 누구나 "페이지 소스 보기"로 확인할 수 있습니다. 모든 채팅 위젯이 이렇게 동작하며, 정상입니다.

알아둘 점:

  • 이 키로 관리 화면에 로그인하거나, 다른 사람의 대화를 읽거나, 구독자 목록을 내보내거나, 봇 설정을 바꿀 수는 없습니다. 오직 하나, 이 봇과 새 대화를 시작하는 일만 가능합니다.
  • 키를 비밀처럼 다루지 마세요. 숨기거나 난독화해도 얻는 것이 없습니다.
  • 키를 교체해야 한다면(예: 외주사와 관계를 정리한 뒤) 지원팀이 처리합니다. 교체 후에는 사이트의 옛 코드가 동작하지 않으므로 갱신해야 합니다.

허용 도메인 목록

위젯에는 허용 도메인 목록(origin allowlist)이 있습니다. 위젯이 동작해도 되는 사이트 주소 목록입니다. 채널을 연결할 때 지정하고, 이후에는 키를 다시 발급하지 않고 변경할 수 있습니다. 사이트에 붙인 코드는 그대로 둡니다.

각 항목은 오리진입니다. 스킴, 호스트, 그리고 표준이 아닐 경우 포트까지입니다.

https://example.com
https://www.example.com
https://shop.example.com
http://localhost:3000

대조는 글자 단위로 정확히 이루어집니다.

  • 와일드카드는 없습니다. https://*.example.com은 동작하지 않습니다. 서브도메인은 하나씩 적으세요.
  • example.comwww.example.com다른 항목입니다. 두 주소로 모두 열린다면 둘 다 넣어야 합니다.
  • http://https://도 다른 항목입니다. 보통 https://만 필요하지만 http:// 테스트 환경은 명시적으로 추가해야 합니다.
  • 경로는 허용되지 않습니다. https://example.com/shop은 거부됩니다. 오리진은 사이트 주소뿐입니다.

실제 도메인이 목록에 없으면 위젯은 시작되지 않습니다. 방문자의 브라우저는 "origin not allowed" 거부를 받고 채팅은 아예 열리지 않습니다. 새 도메인으로 옮기거나, 서브도메인을 추가하거나, 별도 주소로 두 번째 언어 버전을 열었을 때 흔히 나타납니다. 각각 별도 항목이 필요합니다.

목록이 비어 있으면 제한이 없어 어디서나 동작합니다. 이는 "의도적으로 열어둠"이 아니라 "아직 설정하지 않음" 상태로 보세요. 자신의 도메인을 알게 되는 즉시 등록하십시오.

이 제한이 막아 주는 것과 막지 못하는 것

막아 줍니다: 다른 사이트가 여러분의 위젯을 자기 페이지에 심는 일을 막습니다. 그렇지 않으면 누군가 코드를 자기 페이지에 복사하고, 실제 방문자의 브라우저가 봇과 나눈 대화 내용을 그 사이트에 넘기게 됩니다. 목록은 정확히 이 구멍을 막습니다.

막지 못합니다: 키를 복사해 브라우저가 아닌 스크립트 등으로 플랫폼에 직접 요청하는 사람은 막지 못합니다. 사이트 주소를 담은 헤더는 브라우저가 붙이며, 브라우저 밖에서 도는 프로그램은 그것을 생략하거나 임의의 값으로 보낼 수 있습니다. 따라서 이 목록은 임베드 제한이지 보안 경계가 아닙니다. 남용을 막는 것은 플랫폼의 요청 빈도 제한과 대화 관리이지 이 목록이 아닙니다.

모양

위젯모양에서 설정합니다.

  • 위치 — 화면 왼쪽 아래 또는 오른쪽 아래 버튼.
  • 강조 색상 — 버튼과 채팅 요소의 색. 브랜드 색을 쓰면 위젯이 겉돌지 않습니다.
  • 채팅 이름 — 실행 버튼의 라벨과 채팅 패널 제목. 보통 회사 이름이거나 봇이 자기를 소개하는 이름입니다.

옆에는 미리 보기가 있어, 저장 전에 실제 위젯으로 결과를 확인할 수 있습니다.

위젯에는 아직 인사 메시지가 없습니다. 설정할 수 있는 것은 이 세 가지뿐입니다. 첫 메시지는 방문자의 말에 대한 응답으로, 평소의 반응 규칙에 따라 전송됩니다.

변경 사항은 저절로 사이트에 반영됩니다. 위젯이 페이지 로드 시 설정을 읽으므로 붙여 넣은 코드를 고칠 필요는 없습니다.

방문자는 익명입니다

방문자는 아무것도 입력하지 않고 대화를 시작합니다. 처음 실행될 때 위젯은 플랫폼에서 익명 방문자 식별자를 받아 브라우저에 저장합니다. 다시 찾아왔을 때 빈 채팅이 아니라 이전 대화를 보여 주기 위해서입니다.

여기서 따라오는 점:

  • 대화 목록에서 이런 방문자는 익명으로 보입니다. 본인이 쓰기 전까지 이름도, 전화번호도, 이메일도 없습니다.
  • 식별자는 특정 브라우저 안에서만 삽니다. 다른 브라우저, 다른 기기, 사이트 데이터 삭제는 모두 기록이 없는 새 방문자를 뜻합니다.
  • 익명 방문자를 이미 알고 있는 고객, 예를 들어 회원 영역에 로그인한 사용자와 연결할 수 있습니다. 방법은 다음 절에서 설명합니다.

방문자를 자사 사용자와 연결하기

방문자가 이미 사이트에 로그인해 있다면, 그가 누구인지 플랫폼에 알려 줄 수 있습니다. 연결한 뒤에는 대화가 더 이상 익명이 아닙니다. 그 사람의 프로필로 합쳐지고, 이전의 익명 메시지도 사라지지 않습니다.

호출 한 번이면 됩니다. 증명을 만들어 내는 함수와 함께요.

mybot.identify(async (visitorId) => {
  const res = await fetch("/mybot-sign", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ userId: currentUser.id, visitorId }),
  });
  return res.json(); // { userId, signature, expiresAt }
});

위젯은 visitorId — 이 브라우저의 현재 익명 방문자 식별자로, 위젯만 알고 있는 유일한 값 — 와 함께 여러분의 함수를 호출하고, 그 함수가 { userId, signature, expiresAt }를 반환하거나 그 값으로 resolve될 때까지 기다립니다. 함수가 할 일은 오직 visitorId를 로그인한 사용자의 식별자(위의 currentUser.id는 여러분 사이트에서 그게 무엇이든 그 값)와 함께 자사 서버로 전달하고, 서버가 응답한 것을 그대로 돌려주는 것뿐이에요. 실제 서명은 이 함수 안이 아니라 자사 서버에서 이뤄집니다 — 아래를 참고하세요.

미리 계산된 값이나 visitorId를 직접 읽는 방법이 아니라 콜백인 이유는 두 가지예요.

  • 타이밍. 위젯은 시작하는 동안 비동기로 visitorId를 만들어요. 로더 스크립트가 실행되는 순간에는 아직 존재하지 않고, mybot.identify 자체도 그 과정이 끝난 뒤에야 설치돼요(자세한 내용은 아래 참고). 그래서 여러분 페이지가 애초에 mybot.identify를 호출할 수 있는 시점이면, 함수가 받는 식별자는 반드시 진짜예요. 평범한 getter라면 이런 보장이 없을 거예요. 페이지가 그걸 한 줄 이르게 읽고 아무것도 얻지 못한 채, 왜인지 알 방법도 없이 절대 검증을 통과하지 못하는 서명을 조용히 만들어 내는 걸 아무것도 막지 못하니까요.
  • 범위. 여러분 페이지 코드 자체는 visitorId를 손수 들고 있거나 저장하거나 전달할 필요가 전혀 없어요 — 이 값은 오직 이 하나의 함수 안에, 함수가 해야 하는 단 한 번의 호출을 위해서만 존재해요.

visitorId는 위젯을 떠나는 순간부터 소지자 권한(bearer capability)이 됩니다. 그것에 대한 서명을 얻을 수 있는 사람은 누구든 그 방문자의 세션과 합쳐질 수 있어요. 오직 자사 서버로, 자사의 인증된 요청 안에서만 보내고 다른 곳으로는 보내지 마세요 — 로그로 남기지 말고, 제3자에게 전달하지 말고, 클라이언트 쪽 애널리틱스 호출에 넣지 마세요.

서명은 자사 서버에서만 계산합니다

위 함수 안에서 자사 서버가 — 절대 브라우저가 아니라 — 서명을 계산해요: 위젯 비밀 키를 키로 사용해 계산한 HMAC-SHA256 값이며, 16진 표기의 앞 32자, 세 값을 하나의 메시지로 합친 것에 대한 값이에요. 같은 식이 관리 화면의 비밀 키 옆, "Signing algorithm" 아래에도 표시됩니다.

signature = hex(HMAC-SHA256(key: secret, message: userId + "\n" + visitorId + "\n" + expiresAt)).slice(0, 32)

위 식의 각 + "\n" +는 각 부분 사이에 들어가는 실제 줄바꿈 문자를 뜻하며, 백슬래시와 n이라는 두 글자가 아닙니다. 사용 중인 HMAC 함수가 메시지를 하나의 문자열로 받는다면, 세 부분을 실제 줄바꿈으로 이어 붙이십시오. 줄바꿈 없이 .update()를 세 번 따로 호출하면 전혀 다른, 잘못된 메시지를 해시하게 됩니다.

세 부분:

  • userId — 로그인한 사용자의 식별자로, 자사 서버가 위 함수로부터 받아 응답에 그대로 돌려주는 값과 같아요.
  • visitorId — 함수가 인자로 받아 변경 없이 자사 서버로 전달한 익명 방문자 식별자예요.
  • expiresAt — 바로 이 서명이 유효한 시점까지를 나타내는 초 단위 unix 타임스탬프(밀리초 아님)로, 서명할 때 자사 서버가 발급해요. 플랫폼은 expiresAt이 이미 과거인 호출과, 24시간을 초과해 미래인 호출을 모두 거부합니다. 함수에서 반환하기 직전에 서명하고, 이후 요청을 위해 한 번 계산해 캐싱해 두지 마세요.

이 만료 시간이야말로 이번 변경의 핵심이지 부차적인 세부 사항이 아닙니다. 이전 공식은 사용자 식별자만 포함했기 때문에, 한 번 탈취된 서명은 — 어딘가에 로그로 남았든, 트래픽에서 가로챘든, 경위는 상관없이 — 영원히 유효했고 발급 대상이었던 방문자뿐 아니라 어떤 방문자에게도 통했습니다. 그런 서명을 손에 넣은 사람은 완전히 다른 브라우저에서 재사용할 수 있었고, 플랫폼은 낯선 사람의 익명 방문 기록을 실제 고객의 프로필에 병합해 버렸을 것입니다. 서명을 특정 visitorId에 묶고 짧은 수명을 주는 것은 이 구멍의 양쪽을 모두 막습니다. 서명은 발급된 바로 그 세션에서만 검증을 통과하고 다른 어디에서도 통하지 않으며, expiresAt이 지나면 아예 검증을 통과하지 못하게 됩니다. 그래서 서명이 유출되더라도 그것은 영구적인 위협이 아니라 단일 세션에 국한된 짧은 위험으로 그칩니다.

세 값을 모두 자사 서버에서 계산해 함수에서 완성된 값으로 돌려주십시오. 형식적인 당부가 아닙니다. 브라우저에서 서명을 계산하려면 비밀 키 자체를 그곳으로 보내야 하고, 이는 페이지의 모든 방문자에게 건네주는 것과 같습니다. 그 뒤에는 누구든 여러분의 어떤 고객으로도 행세하며 그 대화를 읽을 수 있습니다. 서명(그리고 그 값을 계산한 visitorId/expiresAt)은 브라우저로 돌려줘도 안전하지만 비밀 키는 그렇지 않습니다.

호출 자체는 화면에 아무것도 표시하지 않습니다. 연결은 서버 쪽에서 조용히 이뤄집니다. 함수가 오류를 던지거나 프라미스가 거부되거나, 플랫폼이 결과 서명을 거부하는 경우 — 맞지 않거나, expiresAt이 없거나 이미 지났거나 24시간을 초과해 미래인 경우 — 그 이상 아무 일도 일어나지 않습니다. 방문자는 아무것도 눈치채지 못한 채 익명으로 계속 대화합니다.

mybot.identify는 즉시 존재하지 않습니다. 위젯 본체 번들이 로드되고 진짜 visitorId가 준비된 뒤에 나타납니다(위 "타이밍" 참고). <head>의 첫 줄이 아니라 페이지 로드 핸들러에서 호출하세요.

비밀 키의 위치와 교체 방법

위젯 비밀 키는 여러분이 서명에 쓰는 키입니다. 위젯 설정의 "identify() secret" 섹션에 있습니다. 그 섹션이 위젯이 아직 연결되지 않았다고 하면 웹 채널을 먼저 연결하세요. 비밀 키는 그와 함께 나타납니다.

보기. "Reveal secret" 버튼이 값을 가져옵니다. 값은 평문으로 나오지 않습니다. 먼저 점으로 표시되고, 문자를 드러내는 것은 별도의 "Show" 토글입니다(되돌리려면 "Mask"). 그 옆에는 값을 화면에서 지우는 "Clear from screen" 버튼이 있습니다.

몇 번이든 볼 수 있고, 본다고 해서 무효화되는 것은 없습니다. 화면을 닫은 뒤에도 비밀 키는 그대로 유효합니다. 해시가 아니라 암호화해서 저장하기 때문인데, 매 서명을 검증하려면 실제 값이 필요하기 때문입니다. 반년 뒤 새 백엔드를 배포할 때도 다시 와서 확인하면 됩니다.

복사한 뒤 뒷정리는 직접 하세요. 클립보드는 플랫폼이 비울 수 없습니다. 브라우저에서 이를 확실하게 해내는 방법이 없으므로 가능하다고 말하지 않습니다. 작업이 끝나면 값을 화면에서 지우고 클립보드는 손수 비우세요. 공용 컴퓨터라면 더욱 그렇습니다.

교체. 확인 단계가 있는 "Rotate secret" 버튼입니다. 이것은 즉시 끊는 동작입니다. 새 비밀 키가 생기는 순간 옛 키는 검증을 통과하지 못합니다. 겹치는 구간은 없으며, 두 키가 함께 유효한 순간도 없습니다.

끊기는 것은 방문자를 여러분 사용자와 연결하는 부분뿐입니다. 대화는 중단되지 않습니다. 방문자는 익명 상태로 계속 쓰고 답을 받습니다. 백엔드가 새 키로 서명하기 시작하면 원래대로 돌아옵니다. 설치 코드와 허용 도메인 목록은 그대로여서 사이트에서 바꿀 것은 없습니다.

그러니 교체는 의도를 갖고 하세요. 새 값이 들어간 백엔드 배포를 먼저 준비하고, 그다음에 "Rotate secret"을 누르십시오. 어떻게 되는지 보려고 눌러 보는 것은 좋지 않습니다. 교체 직후 새 값이 화면에 표시되므로 바로 복사할 수 있습니다.

페이지에서의 동작 방식

위젯은 사이트와 충돌하지 않도록 의도적으로 만들어졌습니다.

  • 격리된 컨테이너(닫힌 Shadow DOM) 안에서 동작합니다. 여러분의 스타일이 위젯 안으로 들어가지 않고, 위젯의 스타일도 페이지로 새지 않습니다. 부작용으로 자체 CSS로 위젯 모양을 바꿀 수 없으니, 모양 설정을 사용하세요.
  • 컨테이너는 화면 전체를 덮지만 클릭을 가로채지 않습니다. 클릭은 페이지로 그대로 전달되고, 버튼과 채팅 패널만 반응합니다.
  • 위젯은 항상 콘텐츠 위에 있으며, 여러분의 레이어로 가릴 수 없습니다.
  • 위젯 내부의 오류는 내부에 머뭅니다. 최악이라야 채팅 버튼이 동작하지 않을 뿐 사이트는 계속 돌아갑니다. 위젯이 나타나지 않으면 브라우저 콘솔을 보세요. 대부분 CSP이거나 목록에 없는 도메인이 원인입니다.

위젯이 나타나지 않을 때

순서대로 확인하세요.

  1. 페이지 소스를 열어 두 태그가 있는지, 키가 비어 있지 않은지 확인합니다.
  2. 사이트 주소가 https://www까지 포함해 목록의 값과 정확히 같은지 확인합니다.
  3. 브라우저 콘솔을 봅니다. Content Security Policy 관련 메시지가 있으면 사이트 CSP에서 플랫폼 호스트를 허용해야 합니다.
  4. 광고 차단기가 위젯을 지우고 있지 않은지 확장 프로그램 없는 시크릿 창에서 확인합니다.

다음 문서