리옵트 데이터
수집과 API

데이터 수집과 거버넌스

수집 API의 요청 형식, 수집 모드, 응답과 오류 코드, 중복 제거, 수집한 이벤트의 의미를 관리하는 방법을 설명합니다.

SDK가 대신 해 주는 수집 요청을 HTTP 수준에서 설명합니다. SDK를 쓰지 않고 직접 보내거나, 응답과 오류를 해석해야 할 때 보세요. SDK 설치는 추적 설정에 있습니다.

수집 진입점

  • POST /api/track
  • OPTIONS /api/track

SDK 수집과 직접 HTTP 수집 모두 이 주소 하나로 들어옵니다. 세션 리플레이 녹화만 별도 경로로 올라가며, SDK가 알아서 처리합니다. 세션 리플레이를 보세요.

수집 모드는 요청에 담긴 자격증명으로 정해집니다. 호출자가 따로 지정할 수 없습니다.

헤더모드사용 위치
reopt-write-keybrowser브라우저. 공개되어도 되는 키
reopt-client-id, reopt-client-secretserver서버. 번들에 들어가면 안 되는 키

client id와 secret 중 하나만 보내면 자격증명이 없는 것으로 처리합니다.

방문자 식별에는 두 헤더를 추가로 씁니다.

  • reopt-device-id: 방문자의 device id. browser 모드에서 없으면 서버가 새로 만듭니다.
  • reopt-session-id: 이전 응답으로 받은 <sessionId>.<token> 값. 서버가 발급한 값일 때만 그 세션에 합류하고, 그렇지 않으면 오류 없이 무시하고 device 기준으로 처리합니다.

POST /api/track의 응답에는 x-request-id 헤더가 붙습니다. 문의할 때 이 값을 함께 알려 주세요.

이벤트 형식

모든 이벤트에는 다음 공통 필드가 필요합니다.

필드설명
typetrack, identify, increment, decrement 중 하나
eventIdUUID. 프로젝트 안에서 이 이벤트를 구분하는 값이고, 재전송 중복을 걸러내는 기준입니다
timestamp이벤트가 발생한 시각, millisecond timestamp
payload이벤트 타입별 payload
deviceId선택. server 모드에서만 읽으며, 이 이벤트가 속한 방문자 device를 행 단위로 지정합니다
  • alias 타입은 아직 지원하지 않으며, 요청에 포함되면 400으로 거절됩니다.
  • 이벤트 이름은 200자 이하이고, session_start, session_end, screen_view는 예약어라 쓸 수 없습니다.
  • 이벤트 하나(object)와 비어 있지 않은 이벤트 배열을 모두 받습니다.
  • 요청 body는 512,000바이트를 넘으면 거절됩니다.

browser 모드와 server 모드

항목browserserver
검증 실패 행요청 전체를 400으로 거절그 행만 rejected에 담고 나머지는 처리
짧은 시간 중복 제거적용적용하지 않음. eventId 기준만 사용
배치 안 eventId 중복뒤의 행을 duplicate로 집계뒤의 행을 duplicate_in_batch로 rejected에 담음
device요청 헤더만 사용. body의 deviceId 무시이벤트의 deviceId → 요청 헤더 → profileId 파생 순서
세션device의 세션을 만들고 이어 감이미 있는 세션에 합류만 함. 없으면 세션 없이 기록
봇 판정서버가 요청 헤더로 판정판정하지 않음
응답의 session현재 세션이 있으면 내려줌내려주지 않음

server 모드는 잘못된 행만 거절하므로, 쌓아 둔 로그를 순서대로 다시 보내는 서버가 행 하나 때문에 멈추지 않습니다.

요청이 처리되는 순서

  1. 자격증명을 확인하고 프로젝트의 분당 요청 한도를 확인합니다.
  2. 이벤트 형식을 검증합니다.
  3. browser 모드라면 사람이 보낸 트래픽인지 판정합니다.
  4. 이미 받은 eventId인지 확인하고, browser 모드에서는 짧은 시간 중복 제거를 함께 적용합니다.
  5. 새로 저장할 이벤트만큼 조직의 데이터 포인트를 확인합니다. 한도를 넘으면 요청 전체를 429로 거절하고 아무것도 저장하지 않습니다.
  6. 원본 이벤트를 저장하고 응답을 돌려줍니다.

세션·프로필 정리와 분석 화면 반영은 응답이 나간 뒤에 이어집니다. 전체 흐름은 데이터가 처리되는 방식을 보세요.

사람이 아닌 트래픽

서버가 요청 헤더만 보고 봇인지 판정합니다. 이벤트 속성은 보지 않으므로 호출자가 판정을 바꿀 수 없습니다. 봇이나 의심 트래픽으로 판정된 이벤트는 데이터 포인트를 차감하지 않고, 세션·프로필·분석 화면에 반영하지 않습니다. 건수와 사유는 데이터 카탈로그의 트래픽 품질에서 확인합니다. 이렇게 판정된 요청의 응답에는 x-traffic-class 헤더가 붙습니다.

중복 제거

중복 제거는 두 종류입니다.

종류기준동작
eventId 중복프로젝트 + eventId이미 받은 이벤트는 새로 저장하지 않고 duplicate로 응답합니다.
짧은 시간 중복 제거프로젝트, device, 타입, 이름, payload, 5초 구간같은 방문자가 같은 5초 구간에 보낸 똑같은 이벤트를 저장 전에 제외합니다.

짧은 시간 중복 제거는 브라우저의 중복 클릭을 걸러내는 규칙이며 server 모드에는 적용하지 않습니다. payload가 기준에 들어가므로 이름이 같아도 속성이 다른 이벤트는 모두 저장됩니다.

전송이 실패했는지 확신할 수 없다면 같은 eventId로 그대로 다시 보내세요. 이미 저장된 이벤트는 duplicate로 집계될 뿐 두 번 저장되지 않습니다.

browser 모드에서는 한 가지를 주의하세요. 짧은 시간 중복 제거는 저장하기 전에 적용되므로, 실패한 요청을 5초 안에 똑같이 다시 보내면 중복으로 처리되어 저장되지 않을 수 있습니다. 실패한 요청은 5초가 지난 뒤에 다시 보내세요.

응답 읽는 법

{
  "status": "ok",
  "requestId": "req_...",
  "mode": "server",
  "accepted": 1,
  "duplicates": 0,
  "rejected": [],
  "queued": true
}
필드의미
mode이 요청이 처리된 수집 모드 (browser 또는 server)
accepted이번 요청에서 새로 저장된 이벤트 수
duplicates중복 제거로 저장이 생략된 이벤트 수
rejected거절된 행 목록. 각 항목은 eventId, index, reason을 가집니다. browser 모드에서는 항상 빈 배열
queued분석 화면에 반영할 작업이 예약되었는지 여부
requestId이 요청을 추적하는 ID
sessionbrowser 모드 전용. 다음 요청의 reopt-session-id로 보낼 id와 token
  • server 모드의 2xx 응답에서는 accepted + duplicates + rejected 개수가 보낸 이벤트 수와 같아야 합니다.
  • rejected의 reason은 validation_failed, reserved_name, duplicate_in_batch 중 하나입니다. reserved_name은 예약어일 때뿐 아니라 이벤트 이름이 없거나 200자를 넘을 때도 나옵니다. 다시 보내도 같은 결과가 나오므로 재시도하지 않습니다.
  • accepted가 0이어도 duplicates가 1 이상이면 요청은 정상 처리된 것입니다.
  • queued가 false이면 새로 반영할 이벤트가 없는 요청입니다.
  • session은 요청 시점에 이미 있던 세션을 가리킵니다. 세션의 첫 요청에는 없는 것이 정상입니다.

드물게 202와 status: "accepted"가 돌아올 수 있습니다. 이벤트는 저장됐고 화면 반영만 조금 늦어지는 경우이므로 성공으로 처리하세요.

오류 코드

오류 본문의 code로 분기하세요. error와 message는 문구가 바뀔 수 있습니다.

상태code대응
400invalid_json, empty_batch, alias_unsupported, validation_failed요청을 고칩니다. 재시도해도 같은 결과입니다.
400payload_too_large배치를 나눕니다.
401unauthorized자격증명을 확인합니다. 재시도하지 않습니다.
410project_purging삭제 중인 프로젝트입니다. 전송을 멈춥니다.
429rate_limited잠시 뒤 스스로 풀립니다. Retry-After만큼 기다립니다.
429quota_exceeded월 데이터 포인트 한도 소진입니다. 한도를 올려야 풀립니다.
500internal_error간격을 두고 재시도합니다.

표에 없는 code가 오면 상태 코드로 판단하세요. 4xx는 요청을 고쳐야 하고, 5xx는 간격을 두고 재시도합니다.

두 429는 의미가 다릅니다. rate_limited는 프로젝트당 분당 요청 수 제한이고, quota_exceeded는 사람이 한도를 조정해야 하는 상태입니다. 한도와 사용량은 비용과 보존 정책에서 다룹니다.

직접 HTTP 호출 예시

SDK 없이 호출할 때는 eventId와 millisecond timestamp를 직접 넣어야 합니다. eventId는 이벤트마다 새 UUID로, timestamp는 이벤트가 발생한 시각으로 바꿔서 보내세요.

curl -X POST "https://data.reopt.ai/api/track" \
  -H "Content-Type: application/json" \
  -H "reopt-client-id: $REOPT_CLIENT_ID" \
  -H "reopt-client-secret: $REOPT_CLIENT_SECRET" \
  -d '{
    "type": "track",
    "eventId": "00000000-0000-4000-8000-000000000001",
    "timestamp": 1763769600000,
    "payload": {
      "name": "page_view",
      "properties": { "path": "/home" }
    }
  }'

터미널이나 스크립트에서 보낼 때는 위처럼 서버 자격증명을 쓰세요. write key로 보낸 요청은 browser 모드로 처리되고, 브라우저가 아닌 곳에서 온 요청은 사람이 아닌 트래픽으로 분류됩니다. 이 경우 응답은 200이어도 이벤트 스트림과 분석 화면에는 나타나지 않습니다.

브라우저 SDK의 전송 방식

브라우저 SDK는 다음 기본값으로 동작합니다.

  • 이벤트는 큐에 들어간 뒤 1초(batch.intervalMs)마다 전송됩니다.
  • 큐 길이가 100개(batch.size)에 도달하면 즉시 전송을 시도합니다.
  • 한 요청은 직렬화 기준 400,000바이트(batch.maxBytes)를 넘지 않게 자릅니다. 서버 상한보다 작게 잡은 값입니다.
  • 페이지가 숨겨지거나 닫히면 큐를 먼저 저장한 뒤 keepalive 요청으로 나눠 보냅니다.
  • device id는 기본적으로 쿠키에 저장하고, 전송하지 못한 큐는 localStorage에 저장합니다.
  • localStorage가 막혀 있으면 큐는 메모리에만 남으므로 페이지를 떠날 때 못 보낸 이벤트는 사라질 수 있습니다.
  • 일시적인 전송 실패는 큐로 되돌려 재시도합니다. 429가 아닌 4xx로 거절된 배치는 버리고 콘솔에 한 번 경고합니다.

client는 write key당 페이지에 하나만 만들어집니다. init()을 다시 호출하거나 Provider가 다시 마운트되어도 같은 인스턴스를 재사용합니다. React Provider는 unmount 시 client를 닫지 않습니다. 정리가 필요하면 close()를 직접 호출하세요.

서버 SDK의 전송 방식

서버 SDK는 공개 키 대신 clientId + clientSecret을 사용합니다.

  • Next.js 요청 안에서는 createReopt()가 돌려주는 getReopt()를 씁니다. 방문자의 device 쿠키를 읽어 이벤트를 같은 방문자의 것으로 기록합니다.
  • 워커, 크론, 스크립트처럼 요청이 없는 Node 프로세스에서는 @reopt-ai/data-sdk-server/node의 client를 씁니다.
  • Node client는 trackAndFlush, identifyAndFlush, incrementAndFlush, decrementAndFlush를 제공합니다. 전송이 끝난 뒤에 resolve됩니다.
  • flush()는 큐가 빌 때까지 전송하고, close()는 flush한 뒤 타이머를 멈춥니다. 스크립트는 종료 전에 close()를 호출하세요.
  • 서버 SDK는 큐를 디스크에 저장하지 않습니다. 보내지 못한 이벤트는 프로세스와 함께 사라집니다.

서버리스 함수는 응답을 반환하는 순간 멈출 수 있어서, 큐에만 넣은 이벤트는 전송되지 않은 채 사라질 수 있습니다. Vercel에서는 SDK가 요청의 waitUntil을 자동으로 찾아 응답 뒤에도 전송이 끝나게 합니다. 따로 설치할 패키지는 없습니다. 다른 호스트에서는 waitUntil 옵션으로 호스트의 함수를 넘기거나, await flush() 또는 *AndFlush 계열로 요청 안에서 전송을 끝내세요.

수집한 이벤트의 의미 관리하기

수집이 끝나면 데이터 카탈로그에서 이벤트의 의미를 관리합니다.

  • 표시 이름, 설명, 아이콘, 색상
  • 전환 여부, 태그, 카테고리
  • 상태 (활성, 내부, 보관 등)

수집된 이벤트와 속성 키는 카탈로그에 자동으로 나타납니다. 표시 이름과 설명 같은 정의는 자동으로 채워지지 않으므로 직접 입력합니다. 속성에 대해 자동으로 채우는 것은 이름, 처음·마지막으로 본 시각, 비어 있는 타입뿐이고, 사람이 적은 설명과 숨김 여부는 건드리지 않습니다.

카탈로그는 콘솔뿐 아니라 저장소의 파일로도 관리할 수 있습니다. 파일에서 생략한 필드는 그대로 두는 것이 아니라 기본값으로 처리되니 주의하세요. 자세한 내용은 프로젝트를 코드로 관리하기와 CLI 사용하기를 보세요.

보존 기간

프로젝트의 보존 기간은 하나이고, 원본 이벤트와 분석 데이터에 함께 적용됩니다. 기간을 저장하는 것만으로는 아무것도 지워지지 않고, 관리자가 프로젝트 설정에서 정리를 실행할 때 지워집니다. AI 호출 기록과 세션 리플레이 녹화는 각자의 보존 기간에 따라 자동으로 정리됩니다. 화면에서 설정하는 방법은 비용과 보존 정책을 보세요.

주의할 점

  • 수집된 이벤트가 분석 화면에 바로 보이지는 않습니다. 방금 보낸 이벤트는 이벤트 스트림에서 먼저 확인하세요.
  • 서버리스에서는 waitUntil 또는 *AndFlush 계열로 요청 생명주기 안에서 전송을 끝내세요.
  • 직접 수집하는 서버는 2xx마다 accepted + duplicates + rejected 개수가 보낸 수와 같은지 확인하세요.
  • 문제를 추적할 때는 requestId와 eventId를 함께 기록해 두세요.

다음 단계