콘텐츠로 이동
ABTO 가이드

연동 가이드

이벤트 설계

이벤트 이름과 속성을 어떻게 정하고, ABTO 시스템 이벤트와 어떻게 구분하는지 안내합니다.

이벤트는 성공 지표의 입력값입니다. 이름을 어떻게 짓고 속성을 어떻게 선언하느냐에 따라 나중에 지표를 만들고 숫자를 믿는 일이 쉬워지기도 어려워지기도 합니다.

System Event
  • 이름이 $로 시작: $pageview, $ai_prompt_submitted
  • ABTO가 정의하고, 명시적 SDK helper로만 전송
Custom Event
  • $ 없이 제품의 언어로 이름 짓기: checkout_completed, summary_copied
  • 직접 정의해서 capture로 전송

이벤트 하나를 끝까지 따라가기

섹션 제목: “이벤트 하나를 끝까지 따라가기”

설계에 들어가기 전에 이벤트 하나가 사용자 화면에서 대시보드 숫자가 되기까지 거치는 경로를 먼저 보면 나머지가 훨씬 쉽게 읽힙니다. 경로는 이름 선언, 클라이언트 계측, device_id 연결, 대시보드 조회의 네 단계입니다.

제품 저장소의 abto.events.ts가 시작점입니다. 여기에 없는 이름은 운영 환경에서 버려지므로 건너뛸 단계가 아닙니다.

export const events = defineEvents({
summary_copied: {
properties: {
document_id: { type: 'string', required: true },
},
},
});

클라이언트에는 Event Key(ek-abto-…)만 둡니다. Calling Key를 여기에 두면 서버 전용 키가 번들에 포함되어 사용자 기기로 배포됩니다.

브라우저에서는 선언한 events를 초기화에 넘기고, 행동이 일어나는 지점에서 호출합니다.

export const abto = initAbto({
projectKey: 'ek-abto-...',
apiHost: 'https://api.abto.app',
environment: 'production',
events,
});
abto.capture('summary_copied', { document_id: 'doc-123' });

모바일도 구조가 같습니다. Android는 이렇게 호출합니다.

abto.capture("summary_copied", mapOf("document_id" to "doc-123"))

어느 쪽이든 SDK가 이벤트를 기기 버퍼에 적재한 뒤 전송하므로, 이 호출은 네트워크를 기다리지 않고 즉시 반환됩니다.

여기가 두 데이터를 잇는 결합 지점입니다. 클라이언트 SDK는 설치되는 순간 익명 device_id를 만들어 기기에 보관하는데, 이 값이 제품 행동과 AI 호출을 잇는 유일한 축입니다.

그러므로 서버가 게이트웨이를 호출할 때 같은 값을 실어 보내야 합니다. 브라우저에서는 abto.getIdentity().deviceId, 모바일에서는 abto.deviceId로 읽어 백엔드로 넘긴 뒤 Server SDK의 컨텍스트에 넣습니다.

await abto.withContext(
{ deviceId, featureId: 'review.summary' },
async () => { /* 모델 호출 */ },
);

deviceIdx-abto-device-id 헤더로 나갑니다. 로그인 id처럼 임의의 값을 넣으면 클라이언트가 보낸 device_id와 달라 행동과 호출이 끝내 만나지 못합니다.

응답 단위 반응까지 분석하려면 게이트웨이가 돌려주는 x-abto-request-id를 브라우저로 내려보내세요. 그 응답의 렌더와 복사, 피드백이 같은 request_id로 묶입니다.

두 기록은 서로 다른 경로로 전송됩니다. 호출 비용과 속도, 토큰은 게이트웨이가 호출을 대신 전달하며 남기고, 방금 보낸 이벤트는 클라이언트 SDK가 따로 올립니다. 서버는 이 둘을 device_id로 맞물려 한 화면에 표시합니다.

수신 여부는 성공 지표 화면 아래쪽 이벤트 표에서 확인합니다. 클라이언트가 보낸 이벤트가 자동으로 나타나므로, 표에 이름이 보이면 수신까지 끝났다는 뜻입니다. 보이지 않는다면 아직 한 번도 발생하지 않았거나 선언과 어긋나 버려진 경우이니 FAQ에서 원인을 확인하세요.

표에 이름이 나타나면 그때부터 지표 산출에 쓰입니다. 비율 지표는 호출이 아니라 사람을 세므로, “이 옵션을 받은 사용자 중 몇 퍼센트가 요약을 복사했는가”가 기기 단위로 정확히 집계됩니다.

Custom Event의 이름과 property를 제품 저장소에 선언하고, 그 registry를 초기화에 넘깁니다.

// abto.events.ts — 우리 제품이 보낼 이벤트를 여기서 정합니다
export const events = defineEvents({
checkout_completed: {
properties: {
order_id: { type: 'string', required: true },
value: { type: 'number', required: true },
scale: { type: 'string', enum: ['KRW', 'USD'], required: true },
},
},
});
// 초기화 — 선언한 registry를 넘겨야 검증이 걸립니다
export const abto = initAbto({ projectKey: 'ek-abto-…', events });
// 호출 — 선언한 이름과 property만 타입을 통과합니다
abto.capture('checkout_completed', { order_id, value: 49_000, scale: 'KRW' });

코드에 두면 두 가지를 얻습니다.

  • 이벤트 계약 변경이 PR 리뷰를 거칩니다. 대시보드에서 따로 등록하면 코드와 어긋난 채로 남습니다.
  • 오타와 빠진 값이 배포 전에 잡힙니다. 선언에 없는 이름이나 required 누락은 타입 검사에서 걸립니다.

주문, 문서, 사용자처럼 제품 도메인의 ID가 필요하면 order_id처럼 property로 직접 선언해 싣습니다. 필수 여부도 이 선언이 정합니다.

대시보드에서 합계나 평균을 보려면 valuescale이라는 이름을 써야 합니다. Success Metric이 이 두 이름만 집계 대상으로 읽기 때문입니다. amount, currency 같은 다른 이름으로 보내면 값은 이벤트에 남지만 대시보드에는 전환 건수만 나옵니다.

개발은 느슨하게, 운영은 엄격하게

섹션 제목: “개발은 느슨하게, 운영은 엄격하게”
상황DevelopmentProduction
미등록 event전송하고 발견 경고drop
required/type/enum 위반전송하고 drift 경고drop
스키마에 없는 일반 property전송전송
$ 이름의 Custom Property전송하지 않음전송하지 않음

개발 중에는 미등록 이벤트도 일단 전송하고 경고만 남겨 작업을 방해하지 않습니다. 운영에서는 선언과 다른 이벤트를 버려서(drop) 데이터 오염을 막습니다.

구매나 복사처럼 일반적인 행동은 device_id로 AI 사용과 이어지므로 따로 할 일이 없습니다. “이 응답을 본 사람이 무엇을 했는지”까지 분석하려면 게이트웨이 응답의 x-abto-request-id를 서버에서 받아 브라우저로 내려주세요. 그 응답에 대한 렌더, 복사, 피드백이 같은 request_id로 묶입니다.

SDK는 이벤트를 기기에 보관했다가 전송하고, 서버가 수신을 확인한 뒤에만 지웁니다. 일시적인 네트워크 오류는 자동으로 재시도하고 서버가 중복을 걸러내므로, 연결이 불안정해도 이벤트가 사라지거나 두 번 세어지지 않습니다.

세션은 따로 계측할 필요가 없습니다. 이벤트에 실린 $session_id와 시각을 근거로 세션 범위가 자동으로 계산됩니다.