AI 분류로 자동입금 무한 재시도와 새벽 운영 알람을 잡다
목차
자동입금 확정 워크플로우에 retry 로직을 붙이는 건 흔한 선택이다. 결제 인프라는 일시 오류가 많고, 대부분의 경우 재시도 한 번이면 해결된다. 그래서 관행적으로 "실패 → 큐 재투입"을 기본값으로 잡아두곤 하는데, 이게 나중에 어떻게 터지는지를 이번에 제대로 봤다.
왜 문제가 됐나
메시지 구분 없이 전부 재시도하는 구조는, 실패의 종류가 하나라는 암묵적 가정 위에 서 있다. 일시 오류만 들어온다면 맞는 가정이다. 초기엔 실제로 그랬다. 파트너 수가 적고, 들어오는 실패 유형도 단순했다.
파트너가 늘고 결제대행사가 추가되면서 상황이 달라졌다. 잘못된 계좌번호로 들어온 입금 시도, 이미 만료된 거래에 붙은 확정 요청 같은 케이스가 섞이기 시작했다. 이런 건 아무리 재시도해도 성공할 수 없다. 그런데도 로직 입장에서는 예외가 던져진 것뿐이라 조건 없이 큐에 다시 쌓았다. 결과적으로 동일한 실패 메시지가 수백 번씩 재처리되고, 운영팀이 새벽에 알람 받고 수동으로 해당 건을 종결시키는 일이 루틴이 되어버렸다.
대표적으로 들어오는 메시지가 이런 식이었다.
[입금알림] 거래종료 / 금액불일치 / 한도초과 / 일시오류
겉으로는 비슷해 보이지만 의미는 전혀 다르다. "일시오류"는 재시도 여지가 있지만 "거래종료"나 "금액불일치"는 재시도가 무의미하다. 정규식으로 구분하려 했는데 결제대행사마다 문구 표현이 미묘하게 달라서 케이스가 폭증했다. 파트너 두 곳이 추가되자 분기문이 30개를 넘겼다.
규칙 기반 분류의 문제는 케이스가 선형으로 느는 게 아니라는 거다. 결제대행사 A의 "거래취소"와 결제대행사 B의 "결제실패(시간초과)"가 의미적으로는 같아도 코드에서는 별도 케이스로 등록된다. 운영 이슈가 생길 때마다 분기를 덧붙이는 패턴은 어느 순간부터 유지보수 가망이 없어진다. 이미 그 임계점을 넘겼다고 봤다.
분류 기준 정리와 AI 도입 결정
수동으로 케이스를 뒤져보니 결국 네 종류였다.
| 유형 | 재시도 | 후속 처리 |
|---|---|---|
| 일시 오류 | O | 큐 재투입 |
| 금액 불일치 | X | 파트너 통보 |
| 거래 만료 | X | 영구 실패 마킹 |
| 미식별 | X | 운영 큐로 격리 |
이 표가 사실상 스펙이다. 규칙 기반으로 이 네 가지를 구분하는 게 어렵다는 건 이미 증명됐으니, LLM에 메시지 원문과 최소 컨텍스트만 넘기고 4종 라벨과 신뢰도만 받는 구조로 전환했다.
구현할 때 신경 쓴 것들.
- 프롬프트는 최대한 짧게, 라벨은 enum으로 강제해서 파싱 실패 여지를 없앰
- 신뢰도 0.8 미만은 무조건 미식별로 강등 - 모호한 판단을 시스템이 자체 처리하게 두지 않음
- 동일 메시지 본문은 해시 기반으로 캐싱해서 반복 호출 비용 억제
LLM 응답 구조는 대략 이렇게 잡았다.
{
"label": "TEMP_ERROR | AMOUNT_MISMATCH | EXPIRED | UNIDENTIFIED",
"confidence": 0.92,
"reason": "거래 만료 후 재요청으로 판단, 재시도 불가"
}
reason 필드는 없어도 동작하지만, 운영 큐로 내려간 케이스를 사람이 라벨링할 때 참고가 된다. 왜 미식별로 떨어졌는지 근거가 한 줄 있으면 케이스 보강 속도가 확실히 다르다.
그리고 핵심은 파이프라인 순서였다. 기존 구조는 확정 프로세스가 먼저 돌고 예외가 나면 그냥 큐로 돌아갔다. 이번엔 분류를 확정 프로세스 앞에 끼워넣었다.
parse → classify → retriable? → enqueue : terminate
재시도 가능 라벨이 아니면 큐 재투입 자체를 차단한다. 이 한 줄이 패치의 본체였다. 나머지는 전부 이 결정의 구현 디테일이다.
운영에 붙이고 나서
결과는 꽤 명확했다.
- 새벽 운영 알람 약 70% 감소
- 무한 재시도로 인한 중복 입금 후보 0건
- 미식별 케이스만 운영 큐로 모여서 케이스 보강이 쉬워짐
수치보다 체감되는 변화는 운영팀이 새벽에 호출되는 빈도 자체가 줄었다는 거다. 남은 30%는 진짜 판단이 필요한 케이스인데, 이건 원래 사람이 봐야 하는 케이스다. 시스템이 틀린 게 아니라 오히려 옳게 걸러내고 있다는 뜻으로 읽힌다.
트레이드오프를 솔직히 말하면, LLM 호출 비용이 생겼다. 메시지 양이 많을 때 캐시 적중률이 관건인데, 지금 구조는 본문 해시가 정확히 같아야 캐시를 쓰기 때문에 의미는 같고 표현만 다른 메시지는 미스가 난다. 결제대행사가 "거래종료"라 쓰든 "해당 거래 종료됨"이라 쓰든 해시가 달라진다.
이걸 더 줄이는 방향으로 임베딩 기반 유사 매칭을 보고 있다. 메시지 임베딩을 미리 저장해두고 코사인 유사도가 임계치 이상이면 이전 분류 결과를 재사용하는 방식. 이게 잘 되면 LLM 호출 자체를 많이 줄일 수 있고, 케이스가 충분히 쌓이면 LLM 없이 임베딩 분류기만으로 돌리는 것도 가능해진다. 운영 큐에서 쌓인 라벨링 데이터가 학습 재료가 되는 구조라, 지금 미식별로 내려가는 케이스를 사람이 꼼꼼히 처리해두는 게 나중을 위한 투자이기도 하다.
신뢰도 애매 구간(0.7~0.8)은 지금은 보수적으로 미식별로 내보내고 있다. 데이터가 쌓이면 이 구간을 다시 튜닝할 생각이고, 파트너별 메시지 패턴 통계도 같이 뽑아서 특정 파트너 발 메시지만 신뢰도가 낮은 경향이 있는지 확인하면 그쪽 프롬프트를 따로 맞출 여지도 생긴다.
댓글 0
첫 댓글 달아줘.