예약이 한곳에서만 들어온다면 통합이라는 말 자체가 필요 없습니다. 문제는 소셜 파티 운영사의 예약이 성격이 전혀 다른 세 경로로 들어온다는 데 있었습니다. 하나는 우리가 주기적으로 물어봐서 가져와야 하는 예약 플랫폼이고, 하나는 상대가 알아서 밀어 넣어 주는 제휴 플랫폼이며, 나머지 하나는 참가자가 직접 채운 구글폼입니다. 데이터가 도착하는 방식도, 담긴 모양도 제각각인 이 셋을 어드민의 한 명단으로 합치는 것이 이 글의 주제입니다.
가져오는 채널과 밀려오는 채널
세 채널은 데이터를 전달하는 방향이 다릅니다. 외부 예약 플랫폼은 우리가 주기적으로 조회해야 최신 상태를 알 수 있는 폴링 방식입니다. 반면 제휴 플랫폼은 예약이 생기거나 취소될 때마다 우리 쪽으로 요청을 보내 주는 웹훅 방식입니다. 구글폼은 참가자가 응답을 남기면 시트에 행이 쌓이고 우리는 그 시트를 읽어 옵니다.
이 방향의 차이를 억지로 하나로 맞추려 하지 않았습니다. 폴링 채널은 스케줄러가 정해진 간격으로 조회해 변경을 반영하고 웹훅 채널은 요청을 받는 수신부를 따로 두어 들어오는 즉시 처리합니다. 웹훅 수신은 폴링과 완전히 분리해 한쪽의 지연이나 장애가 다른 쪽으로 번지지 않게 했습니다. 서로 다른 리듬으로 도는 두 흐름을 굳이 같은 루프에 넣지 않는 것이 안정성의 핵심이었습니다.
밀려오는 데이터에는 중복이라는 함정이 따라옵니다. 웹훅은 같은 이벤트를 여러 번 보내는 경우가 드물지 않고 결제 완료와 사후 설문 완료처럼 한 참가자에게 여러 신호가 순차로 도착하기도 합니다. 그래서 예약 식별자를 멱등 키로 삼아 같은 식별자로 들어온 정보는 새 행을 만들지 않고 기존 행에 병합해 갱신합니다. 신호가 몇 번을 오든 명단에는 참가자 한 명이 한 줄로만 남습니다.
모양이 다른 데이터를 같은 스키마로
방향만 다른 게 아니라 담긴 모양도 달랐습니다. 예약 플랫폼은 예약과 주문, 티켓 구조로 데이터를 주고 제휴 플랫폼은 설문 응답 안에 이름과 성별, 연락처가 흩어져 있는 형태로 보냅니다. 구글폼은 더 사정이 복잡했습니다. 운영 중에 폼 양식이 여러 번 바뀌면서 같은 시트 안에 옛 양식과 새 양식의 열이 뒤섞여 있었습니다.
이 이질성을 흡수하는 것이 정규화 계층의 일입니다. 각 채널마다 전용 어댑터를 두어 그 채널의 고유한 모양을 읽어 공통 스키마로 옮깁니다. 구글폼 어댑터는 행마다 어느 양식의 열이 채워져 있는지를 보고 옛 양식과 새 양식을 구분해 읽고 프로필 항목처럼 나중에 추가된 열은 보조 데이터로 함께 흡수합니다. 채널이 무엇이든 정규화를 통과하고 나면 이름, 성별, 연락처, 회차, 결제 상태 같은 동일한 필드를 가진 예약 한 건이 됩니다.
이렇게 공통 스키마로 모으면 그 위에 얹히는 기능들이 채널을 신경 쓸 필요가 없어집니다. 입금 대조도, 문자 발송도, 투표 매칭도 예약이 어디서 왔는지 묻지 않고 통합 로스터만 바라봅니다. 채널별 차이는 정규화 계층에서 모두 끝나고 그 뒤 로직은 단일한 데이터만 다룹니다.
취소와 예외를 다루는 원칙
통합에서 가장 조심스러운 부분은 취소였습니다. 채널마다 취소를 표현하는 방식이 다르고 한 채널의 취소를 다른 채널에까지 잘못 전파하면 사고가 됩니다. 그래서 원칙을 세웠습니다. 각 채널의 취소는 그 채널에서 온 예약의 상태만 바꾸고 다른 채널의 원본 예약에는 손대지 않습니다. 제휴 플랫폼에서 온 취소가 외부 예약 플랫폼의 예약 상태를 건드리는 일은 절대 일어나지 않도록 막았습니다.
예상치 못한 형태의 데이터도 조용히 넘기지 않았습니다. 아직 지원하지 않는 유형의 이벤트가 들어오면 처리 가능한 부분만 반영하고 나머지는 경고 로그로 남깁니다. 덕분에 새로운 예약 유형이 등장했을 때 언제 어떤 모양으로 들어왔는지 기록이 남아 기능을 추가할 때 근거로 삼을 수 있었습니다.
세 채널을 하나로 모으는 이 로스터가 시스템의 토대입니다. 이 위에서 도는 입금 대조 이야기는 입금 자동 대조 설계에서, 공식 API 없는 예약 플랫폼을 어떻게 붙였는지는 예약 플랫폼 GraphQL 역공학 연동에서 다룹니다. 전체 그림은 소셜 파티 운영 자동화 시스템 사례에 있습니다.
자주 묻는 질문
폴링과 웹훅을 왜 분리했나요?
두 방식은 도는 리듬이 다릅니다. 폴링은 정해진 간격으로 우리가 조회하고 웹훅은 상대가 보낼 때 즉시 처리해야 합니다. 이 둘을 같은 루프에 넣으면 한쪽의 지연이 다른 쪽을 막을 수 있어서 웹훅 수신부를 폴링과 완전히 분리해 서로 영향을 주지 않도록 했습니다.
같은 예약이 중복으로 들어오면 어떻게 되나요?
예약 식별자를 멱등 키로 사용합니다. 같은 식별자로 여러 번 정보가 도착해도 새 행을 만들지 않고 기존 행에 병합해 갱신하므로 명단에는 참가자당 한 줄만 남습니다. 결제 완료와 사후 설문처럼 여러 신호가 순차로 오는 경우에도 하나의 예약으로 모입니다.
폼 양식이 바뀌어도 과거 데이터를 읽을 수 있나요?
읽을 수 있습니다. 구글폼 어댑터가 행마다 어느 양식의 열이 채워져 있는지 보고 옛 양식과 새 양식을 구분해 처리합니다. 양식이 바뀌면서 열 구조가 달라져도 각 행을 올바른 규칙으로 해석해 같은 공통 스키마로 옮깁니다.