전송 동작
표준 페이로드, 5초 제한 시간, 재시도가 없는 이유와 각 전송 상태의 의미를 설명합니다.
웹훅 전달 방식은 의도적으로 단순하게 설계되어 있으며, 이 단순한 방식은 수신 측 구현에 영향을 줍니다. HTTP 요청은 1번만 시도하며, 제한 시간은 5초이고, 재시도는 없습니다. 핸들러가 요청 처리 중에 동기적으로 수행할 작업의 범위를 정하기 전에 이 페이지를 읽어 보시기 바랍니다.
표준 페이로드
listing.submission을 제외한 모든 이벤트는 동일한 최상위 구조로 전달되며, 이벤트별 필드는 data 아래에 중첩됩니다.
{ "event": "connection.location_connected", "timestamp": "2026-08-25T14:32:10Z", "account_id": 11073, "location_id": "279381", "data": { "platform": "google", "connected_account_id": "ba03bc4b-9f8c-4d3b-8e93-9628034c63cc" }}data 내의 선택적 필드는 값이 비어 있으면 null로 전송되지 않고 생략됩니다. 이러한 경우를 고려하여 코드를 작성해야 합니다. 누락된 키와 명시적인 null를 동일하게 처리하고, 선택 사항으로 문서화된 키가 항상 존재한다고 가정하지 않아야 합니다.
2가지 예외
5초 제한 시간
각 전달은 단일 POST 요청이며, 연결 제한 시간은 5초이고 읽기 제한 시간도 5초입니다. 엔드포인트가 시간 내에 응답하지 않으면 시도를 중단하고 timeout로 기록합니다.
이 제한 시간은 핸들러의 전체 처리에 적용됩니다. 안전한 설계 방식은 다음뿐입니다.
- 서명을 검증합니다.
- 큐, 테이블, 로그 등 영구 저장이 가능한 곳에 페이로드를 기록합니다.
200을 반환합니다.
그 밖의 모든 작업(데이터 보강, 여러 대상으로 작업 분배, REST API 재호출, 타사 서비스와 연동하는 모든 작업)은 큐에서 꺼낸 뒤에 처리해야 합니다.
재시도는 없습니다
전달은 정확히 1번만 시도합니다. 백오프, 재시도 큐, 데드 레터 큐는 없습니다. 엔드포인트가 2분 동안 중단되면 그 2분 동안 발생한 이벤트는 유실됩니다. 해당 이벤트는 나중에도 전달되지 않습니다.
이에 따른 다음 2가지 결과를 명확히 이해해야 합니다.
- 리디렉션을 따르지 않습니다. 리디렉션은 완전히 비활성화되어 있으므로 엔드포인트가
301또는302을 반환하면 다른 URL로 이동하지 않고 전달 실패로 기록합니다. 웹훅 URL을 최종 URL로 설정해야 합니다. 많은 웹 서버가 기본적으로 추가하는 기본 도메인에서www로의 리디렉션과 후행 슬래시 리디렉션에 유의해야 합니다. 2xx만 전달 성공으로 간주합니다.3xx,4xx,5xx은 모두 실패로 기록됩니다.
중복 및 순서
- 같은 이벤트를 2번 수신할 수 있으며, 서로 다른 2개의 변경에 같은 id가 사용될 수 있습니다.
data에 포함된 식별자(interaction id, post item id, connected-account id)는 변경이 아니라 리소스를 식별합니다.interaction.review는 리뷰가 변경될 때마다 같은 interaction id로 다시 발생하며, 어떤 페이로드에도 버전이나 updated-at 필드가 없으므로 id +event를 기준으로 영구적으로 중복을 차단하는 핸들러는 실제 수정 사항까지 알림 없이 버립니다. 대신 완전히 동일한 중복을 감지합니다. 원시 본문 바이트의 해시를 계산하고, 짧고 제한된 시간 구간(영구적이 아닌 몇 분 단위) 안에서 같은 해시가 반복되면 무시합니다. 같은 id라도 본문이 다르면 중복이 아니라 새로운 변경입니다. 그런 다음 REST API에서 리소스를 다시 조회하고 그 조회 결과를 기준으로 삼습니다. 쓰기를 건너뛰지 말고 리소스 id를 키로 사용해 저장합니다. - 순서는 보장되지 않으며,
timestamp로도 이를 해결할 수 없습니다. 이벤트는 여러 독립적인 경로에서 생성되어 도착하는 대로 전달되므로, 먼저 올 것으로 예상한local_post.created보다local_post.published가 먼저 도착할 수 있습니다. 같은 리소스에 대한 2개의 변경을timestamp기준으로 정렬해서는 안 됩니다. 이 값은 변경이 발생한 시점이 아니라 전달할 데이터를 구성하는 시점에 기록되며, 페이로드 어디에도 버전이나 시퀀스 번호가 없습니다. 순서가 중요하다면 리소스를 다시 조회하고 API가 반환하는 값을 사용합니다. profile.updated에는 디바운스가 적용되어 사업장별로 60초마다 1번만 전달되며, 시간 구간의 시작 시점에 전달됩니다. 이는 전달을 지연하는 것이 아니라 실제로 여러 수정을 하나로 줄이는 방식입니다. 첫 번째 수정이 시간 구간을 시작하면서 전달되고, 해당 구간 내의 모든 수정은 버려져 전달되지 않으며,changed_fields에는 시간 구간을 시작한 수정만 기술됩니다. 따라서 이벤트는 시간 구간의 시작에 도착하므로, 이벤트 도착을 계기로 다시 조회하면 첫 번째 수정만 확인할 수 있습니다. 이벤트 발생 후 최소 60초가 지난 시점인 시간 구간 종료 후에 두 번째 재조회를 예약하고, 타이머에 따라 사업장 데이터도 대조하여 동기화합니다. 알림 자체가 유실될 수도 있으며, 전달은 1번만 시도하고 재시도하지 않기 때문입니다. 그 외에는 디바운스가 적용되지 않습니다.
누락된 이벤트 복구
재시도와 재전송이 없으므로 모든 이벤트 유형에 대해 대체 수단으로 사용할 수 있는 신뢰할 수 있는 조회 방법이 필요합니다. 문제가 있어 보일 때만이 아니라 정해진 일정에 따라 데이터를 대조하여 동기화합니다.
전송 결과
외부로 요청을 보내지 않은 시도를 포함하여 모든 시도는 상태와 함께 기록됩니다. 아래는 실제로 기록되는 정확한 문자열입니다.
수신 측 점검 목록
- 모든 요청의 서명을 검증하고, 일치하지 않으면
401를 반환하며 아무것도 처리하지 않습니다. - 원본 본문 바이트를 해시하며, 다시 직렬화한 객체는 절대 해시하지 않습니다.
- 5초보다 충분히 짧은 시간 안에
2xx를 반환하고 작업은 큐에 저장합니다. - 반복 처리해도 같은 결과가 나오도록
data에 포함된 ID를 각 핸들러 쓰기 작업의 키로 사용하되, 반복된 id를 처리할 작업이 없다는 의미로 간주해서는 안 됩니다. - 도착 순서로 순서를 추론하지 않으며,
timestamp를 기준으로 정렬하지 않습니다. - 수신 시
location_id를 문자열로 정규화합니다. - 인식할 수 없는
event값은 무시하되,200는 반환합니다. - 웹훅 URL은 리디렉션 없이 최종 URL로 제공합니다.
- REST API와 주기적으로 대조하여 불일치를 해소합니다. 재시도로 복구할 수는 없습니다.