대규모 환경에서의 실시간 사기 스코어링
tracio.ai가 스트림 처리, 사전 계산된 시그널 벡터, 엣지 캐싱을 활용해 초당 5만 건의 이벤트를 50ms 미만의 스코어링으로 처리하는 방법.
대규모 환경에서의 사기 스코어링은 배치 처리와는 근본적으로 다른 아키텍처를 요구합니다. 결제가 승인되거나 계정이 생성되는 순간, 위험 점수를 전달하는 데 주어진 시간은 몇 분이 아니라 몇 밀리초입니다. tracio.ai에서는 초당 5만 건 이상의 이벤트를 스코어링 지연 시간 중앙값 22ms로 처리합니다. 이 글에서는 이를 가능하게 하는 아키텍처를 설명합니다.
스코어링 파이프라인
들어오는 모든 이벤트는 시그널 강화, 벡터 계산, 위험 스코어링으로 이루어진 3단계 파이프라인을 거칩니다. 시그널 강화는 방문자의 핑거프린트, 봇 탐지 결과, IP 인텔리전스, 과거 행동 등 디바이스 인텔리전스 데이터를 원본 이벤트에 덧붙입니다. 벡터 계산은 이렇게 강화된 시그널을 스코어링 모델에 최적화된 고정 길이의 특징 벡터로 변환합니다. 위험 스코어링은 이 벡터를 학습된 모델에 통과시켜 0.0에서 1.0 사이의 점수를 반환합니다.
핵심적인 설계 결정은 강화 및 벡터 계산을 스코어링과 분리한다는 점입니다. 강화 데이터는 사전에 계산되어 캐싱됩니다. 방문자가 페이지를 로드하면 해당 디바이스 프로필을 계산해 TTL 60분으로 Redis에 저장합니다. 스코어링 요청이 도착하면(대개 결제나 로그인에 의해 트리거됩니다) 프로필을 다시 계산하는 대신 사전 계산된 프로필을 가져옵니다. 이로써 스코어링 지연 시간이 200ms 이상에서 30ms 미만으로 줄어듭니다.
Go를 활용한 스트림 처리
수집 계층은 Go로 작성되었으며 팬아웃(fan-out) 아키텍처를 사용합니다. 들어오는 이벤트는 HTTP POST로 도착해 즉시 내부 채널에 배치됩니다. 워커 고루틴 풀이 이 채널에서 데이터를 읽어 강화 작업을 수행하고, 강화된 이벤트를 분석용 ClickHouse와 실시간 처리를 위한 스코어링 큐에 기록합니다. 팬아웃 풀은 큐 깊이에 따라 동적으로 확장됩니다.
수집 계층에 Go를 선택한 이유는 뛰어난 동시성 프리미티브와 예측 가능한 메모리 할당 때문입니다. 각 워커 고루틴은 약 4KB의 스택 공간을 사용하므로 단일 노드에서 수천 개의 동시 워커를 실행할 수 있습니다. 밀리초 미만인 가비지 컬렉터의 일시 정지는 높은 처리량에서 일관된 지연 시간을 유지하는 데 결정적입니다.
엣지 캐싱과 시그널 벡터
거래량이 가장 많은 고객을 위해서는 사전 계산된 시그널 벡터 캐시를 사용해 엣지에 스코어링 모델을 배포합니다. 디바이스가 처음 관측되면 전체 시그널 벡터를 계산해 엣지 캐시(Cloudflare Workers KV에 배포됨)에 저장합니다. 동일한 디바이스에 대한 이후의 스코어링 요청은 캐싱된 벡터를 가져와 엣지에서 로컬로 스코어링을 실행하며, 10ms 미만의 지연 시간을 달성합니다.
엣지 스코어링 모델은 전체 모델을 경량화한 버전으로, 더 작고 빠르지만 동일한 정확도 목표에 맞춰 최적화되어 있습니다. 엣지 모델은 매주 재학습하고, 캐시 무효화 폭주를 피하기 위해 롤링 배포로 업데이트를 배포합니다. 엣지 모델의 신뢰도가 설정 가능한 임계값 아래로 떨어지는 경우에는 서버 측에서 전체 모델을 실행합니다.
분석을 위한 ClickHouse
강화된 모든 이벤트는 컬럼 지향 분석 데이터베이스인 ClickHouse에 저장됩니다. ClickHouse의 압축 및 쿼리 성능 덕분에 수십억 건의 이벤트를 저장하면서도 실시간 분석 쿼리를 지원할 수 있습니다. 고객은 이러한 분석을 활용해 사기 패턴을 파악하고, 스코어링 임계값을 조정하며, 개별 이벤트를 조사합니다.
ClickHouse에서는 국가별 사기율, 디바이스 유형별 스코어링 분포, 임계값별 오탐률 등 사전 집계된 지표를 유지하기 위해 구체화 뷰(materialized view)를 사용합니다. 이 구체화 뷰는 이벤트가 도착하는 대로 실시간으로 갱신되어, 비용이 큰 집계 쿼리 없이도 대시보드에 바로 쓸 수 있는 지표를 제공합니다.
배운 교훈
실시간 스코어링 시스템을 구축하며 몇 가지 교훈을 얻었습니다. 첫째, 사전 계산이 가장 중요한 최적화입니다. 스코어링 요청이 도착하기 전에 할 수 있는 작업은 모두 지연 시간 예산에 포함되지 않는 작업이기 때문입니다. 둘째, Go의 동시성 모델은 높은 처리량의 이벤트 처리에 적합하지만, GC 부담을 피하려면 메모리 할당에 대해 엄격해야 합니다. 셋째, 엣지 배포는 지연 시간 측면에서 획기적이지만, 오래된 예측을 피하기 위해서는 세심한 모델 관리가 필요합니다.