순서가 뒤섞인 웹훅에서 최종 상태를 결정하는 법
웹훅은 도착 순서가 업무 발생 순서와 같다고 보장하지 않습니다. 이벤트 의미와 엔터티 식별자를 기준으로 상태를 해석하고, 늦게 도착한 낡은 이벤트가 최신 상태를 덮지 않게 하는 점검법을 안내합니다.
순서가 뒤섞인 웹훅을 다룰 때 수신 시각만 보고 마지막 레코드를 최신 상태로 취급하면 안 됩니다. 먼저 entity.id나 chatId로 같은 대상을 묶고, event가 의미하는 상태 전이를 정한 뒤, 허용된 전이만 적용해야 합니다. 이 저장소의 현재 컨트롤러는 모든 원본 봉투를 received_at과 함께 보관하지만, 업무 요청 생성은 메시지 이벤트와 고유 ID를 기준으로 제한합니다.
사건 타임라인: 먼저 도착한 update
가상 웹훅 사례로, 네트워크 재시도·큐 지연 때문에 update가 먼저 도착하고 upsert가 나중에 도착하는 상황을 분석합니다. 이는 현재 ChannelTalk 계약이 update·upsert를 제공한다는 주장이 아닙니다. 공식 문서의 event와 entity 개념을 읽고 각 event가 생성·변경·삭제 중 무엇을 뜻하는지 공급자 계약으로 확인해야 합니다.Webhook 이벤트 계약만으로 우리 서비스의 상태 전이를 추론하지 말고 저장소 테스트와 함께 판정하세요.

영향 범위와 현장에서 남은 증거
대상 키: entity.id 또는 chatId처럼 같은 대상을 재식별할 값
이벤트 의미: upsert·update·push·delete 등 공급자가 보낸 event
관찰 시각과 처리 결과: received_at, 원본 저장 성공, 업무 반영 여부
원인 분석: 같은 대상의 두 이벤트
가상 사례의 입력은 같은 entity.id=chat-9에 대해 10:02 update, 10:03 upsert가 도착한 것입니다. 단순한 도착 순서 규칙은 upsert를 최신이라고 보지만, 실제 결정은 이벤트 계약에 따라 달라집니다. 중간 산출물로 두 원본을 모두 저장하고, 상태 전이 표에서 upsert가 초기 존재를 보장하는지 확인합니다. 존재하지 않는 대상을 update가 먼저 만들 수 없다면 update는 원본만 남기고 upsert에서 업무 상태를 생성합니다. 기대 결과는 최종 상태가 한 행으로 재현되고, 어떤 이벤트가 승격을 일으켰는지 추적되는 것입니다.

복구 후 재발 방지 판정
가장 위험한 실패는 낡은 update가 이미 처리된 최신 정보를 덮어쓰는 것입니다. 이벤트에 공급자 시퀀스나 updatedAt이 없다면 없는 값을 만들어 정렬하지 말고, 원본을 보관하며 충돌을 보류하세요. 운영자는 원본 두 건을 비교해 수동으로 복구하거나, 공식 계약에서 제공하는 버전 필드를 추가로 매핑해야 합니다. 삭제 이벤트도 즉시 hard delete하지 말고 재처리·감사에 필요한 원본을 보존하는 대안을 검토합니다.
사건 종료 기록
같은 대상의 가능한 이벤트 순열을 fixture로 만든다.
각 순열에서 최종 상태와 보관된 원본을 비교한다.
계약에 없는 순서 충돌은 오류로 숨기지 않고 보류 사유로 기록한다.
완료 조건은 같은 대상의 이벤트 순서를 바꾸어 재현해도 허용된 전이만 상태를 바꾸고, 충돌·실패 입력은 원본과 복구 사유가 남는 것을 확인하고 기록하는 것입니다.