파트너 충전 입금자명 미스매칭 건수 감소
목차
파트너 충전 매칭 로직에서 입금자명 미스매칭이 슬금슬금 늘고 있었음. 처음엔 그냥 넘겼는데, 수동 처리 큐가 쌓이기 시작하면서 CS팀에서 신호가 왔다. 들어오는 입금 알림 SMS를 파싱한 결과를 뜯어보니 입금자명 자리에 "잔액", "수수료", "농협" 같은 토큰이 버젓이 박혀 있었음. 매칭 실패 → 수동 처리 큐 적체 → CS 부담 증가. 깔끔한 도미노였다.
파싱 버그인 줄 알고 정규식부터 의심했는데, 까보니 토큰 분리 자체는 정상이었음. 문제는 "어떤 토큰을 사람 이름으로 인정할 것인가" 판단부가 사실상 비어있었다는 것. 기존 로직은 "입금" 키워드 뒤 첫 번째 토큰을 무조건 입금자명으로 취급했는데, 결제대행사마다 SMS 포맷이 제각각이라 안내 문구가 먼저 나오는 케이스에서 그냥 박살이 났다.
오탐이 생기는 구조
실제로 수집된 샘플을 보면 패턴이 보인다.
| 케이스 | 원문 일부 | 추출 결과 |
|---|---|---|
| 정상 | "입금자 홍길동 1,000원" | 홍길동 |
| 오탐 A | "입금 잔액 5,000원" | 잔액 |
| 오탐 B | "입금 수수료 차감 후" | 수수료 |
| 오탐 C | "농협 입금 ABC123" | 농협 |
오탐 C 케이스가 특히 재밌는데, 은행명이 "입금" 앞에 나오는 포맷을 쓰는 대행사가 있다. 파서는 역순으로 읽을 능력이 없으니 그냥 "농협"을 입금자로 확정해버림. 이런 포맷 다양성은 대행사를 하나씩 붙일 때마다 생겨나는 거라, 처음 설계할 때 "입금 뒤 첫 토큰" 가정이 얼마나 취약한지를 너무 낙관적으로 봤던 것 같다.
SMS 파싱 파이프라인이 이런 구조를 가지면, 새 대행사가 붙을 때마다 오탐 케이스가 누적된다. 파서는 아무것도 모르고 매번 잘못된 토큰을 뱉고, 매칭 로직은 그걸 그대로 믿는다. 매칭이 실패하면 수동 큐로 떨어지고, 수동 큐는 사람이 손으로 처리해야 한다. 규모가 작을 때는 묻히는데, 대행사 수가 늘면 선형 이상으로 터진다.
수정 내용
이번 수정의 방향은 블랙리스트 키워드 셋 도입이다. 후보 토큰이 셋에 걸리면 다음 토큰으로 슬라이드하는 방식.
EXCLUDE_TOKENS = {
"잔액", "수수료", "이체", "출금", "환불",
"농협", "국민", "신한", "우리", "하나", "기업"
}
def extract_sender_name(tokens: list[str]) -> str | None:
for token in tokens:
if token in EXCLUDE_TOKENS:
continue
if len(token) <= 1:
continue
if token.isdigit():
continue
if token.isascii() and len(token) >= 6:
continue
return token
return None
추가로 넣은 가드 조건이 세 가지다.
- 한 글자 토큰 스킵: 조사나 단음 노이즈가 빠져나오는 케이스 방어
- 숫자만으로 이루어진 토큰 스킵: 금액이나 계좌 뒷자리가 입금자로 오탐되는 케이스
- 영문 6자 이상 코드성 문자열 스킵: 거래코드(예: TX202503 같은 것)가 입금자 자리에 들어오는 오탐 방지
가드 조건들이 조금 휴리스틱하게 느껴질 수 있는데, 의도적으로 그렇게 뒀다. 이 함수의 역할은 "확실히 사람 이름이 아닌 것"을 걸러내는 것이고, 판단을 못 하겠으면 None을 리턴해서 수동 큐로 보내는 게 낫다. 잘못된 이름으로 매칭하는 것보다 수동 처리가 훨씬 낫기 때문이다.
회귀 방지와 테스트 설계
수정 자체보다 테스트 픽스처 설계에 시간을 더 썼다. 실제 결제대행사 4사 샘플 메시지 50여 건을 픽스처로 박아뒀고, 정상 케이스와 오탐 케이스를 한 번에 돌리게 했다.
- 정상 메시지: 입금자명이 정확히 추출되는지
- 오탐 케이스: 블랙리스트가 실제로 걸러내는지
- 엣지: 입금자명에 공백이나 특수문자가 섞인 경우
이렇게 해두면 나중에 새 대행사 포맷이 들어왔을 때 어디가 깨지는지 바로 보인다. 파서를 고칠 때마다 기존 케이스를 돌려보는 것만으로 회귀 여부를 확인할 수 있다. 테스트가 없었다면 이번 수정도 "아마 됐겠지" 수준으로 밀었을 텐데, 50건을 한꺼번에 돌리면서 엣지 케이스 두 개를 추가로 잡아냈다.
블랙리스트의 본질적 한계
블랙리스트 방식은 새 결제대행사가 붙거나 SMS 템플릿이 바뀌면 또 깨진다. 이건 구조적 한계라 어쩔 수 없음. 본질적으로는 "한글 2-5자 + 허용 영문 패턴" 같은 화이트리스트로 가는 게 맞고, 화이트리스트가 블랙리스트보다 안정적인 이유는 "허용할 것만 정의"하기 때문이다. 세상에 있는 은행명과 안내 문구를 전부 블랙리스트에 넣을 수는 없지만, 한국 개인 입금자명의 패턴은 꽤 좁게 정의할 수 있다.
다만 화이트리스트에도 약점이 있음. 외국인 입금자명이나 법인명 케이스를 놓친다. 이름 규칙이 다른 케이스를 전부 화이트리스트로 커버하려면 리스트 자체가 복잡해지고, 그게 또 관리 포인트가 된다. 그래서 다음 사이클에는 입금자명 후보를 별도 분류기로 빼서 한글 개인명/법인명/외국인명 케이스를 각각 처리하는 구조로 다듬을 생각이다.
이번 커밋은 급한 불을 끄는 핫픽스 성격이고, 그 점을 커밋 메시지에도 명시해뒀다. 완벽한 해결이 아니라 즉각적인 개선이 목적이었고, 배포 후 매칭 실패 건수가 눈에 띄게 줄었다는 게 위안이다. 좋은 시스템은 한 번에 만들어지지 않는다 — 핫픽스를 쌓다 보면 구조가 보이고, 구조가 보여야 제대로 된 설계를 할 수 있다.
댓글 0
첫 댓글 달아줘.