인터넷은행 송금 핸들러 추가와 실패 알림 메시지 개선
목차
연락처 송금 기능에 인터넷은행 하나를 붙이는 작업이었다. 규모만 보면 별거 아닌데, 들어가니까 생각보다 손댄 곳이 많아서 정리해둔다.
핸들러 레지스트리 구조와 이번 변경
기존 구조는 은행 코드를 키로 핸들러를 맵에 등록하는 방식이었다. 시중은행 몇 군데가 먼저 붙어 있었고, 추가할 때마다 키 하나 등록하고 핸들러 클래스 하나 만드는 패턴으로 굳어져 있었음.
처음엔 간단할 줄 알았다. 기존 시중은행 핸들러 복사하고 엔드포인트랑 헤더 몇 줄 바꾸면 끝일 거라고 생각했는데, 막상 들어가 보니 인터넷은행 응답 포맷이 미묘하게 달랐다. 계좌 유효성 체크 결과를 담는 필드 이름부터 달랐고, 에러코드 체계도 별개로 굴러가고 있었다. 기존 핸들러가 암묵적으로 가정하던 포맷을 그냥 넘겨줬을 때, 파싱 단계에서 조용히 뻗는 게 문제였음.
결국 핸들러 인터페이스에 변환 계층을 하나 더 두는 방향으로 정리했다. 외부 응답을 내부 도메인 객체로 변환하는 메서드를 인터페이스 계약으로 올리고, 각 핸들러가 자기 포맷에 맞게 구현하도록 바꿨다. 대략 이런 형태다.
interface BankTransferHandler {
fun parseAccountValidation(raw: String): AccountValidationResult
fun parseTransferResult(raw: String): TransferResult
fun mapErrorCode(bankCode: String): TransferFailureReason
}
이 계층이 없으면 핸들러마다 파싱 로직이 제각각 흩어지고, 새 은행 붙일 때마다 기존 코드를 뒤져가며 '이 핸들러는 어떻게 했지?' 하고 확인하게 된다. 계약을 명시적으로 잡아두면 컴파일 단계에서 빠진 구현이 잡히고, 리뷰할 때도 같은 체크포인트를 보게 된다.
유틸 세 군데 손본 이유
핸들러 작업 하면서 연관된 유틸 코드가 눈에 밟혀서 같이 손봤다.
매칭 유틸은 동일 전화번호로 파트너가 여럿 등록된 케이스를 다루는 로직인데, 우선순위 처리가 비어 있는 분기가 있었다. 운영 데이터 보니 이 케이스가 생각보다 꽤 있었음. 중복 파트너 중 어떤 걸 선택할지 기준이 없으면 결과가 비결정적으로 나올 수 있어서, 가장 최근 등록된 파트너를 우선하는 로직을 명시적으로 채워뒀다.
송금 유틸은 금액 포맷팅 코드가 분기마다 조금씩 다른 형태로 흩어져 있었다. 천 단위 콤마 찍는 거, 소수점 처리 방식이 분기별로 미묘하게 달라서, 같은 금액이 화면마다 다르게 찍힐 여지가 있었음. 단일 함수로 모으고 포맷 옵션을 파라미터로 받도록 통합했다.
알림 유틸이 이번 작업 중 손이 제일 많이 갔다. 그동안 송금 실패 알림 메시지가 FAIL_03, ERR_BANK_07 같은 코드를 그대로 던져줬는데, 운영팀이 매번 코드표 찾아보고 있는 상황이었다. 문서화된 코드표도 최신 상태인지 불확실했고.
이번에 매핑 테이블을 코드 안에 정의하면서, 메시지에 사유 설명과 다음 액션 가이드를 같이 담도록 바꿨다.
| 실패 분류 | 대표 케이스 | 알림 메시지 방향 |
|---|---|---|
| 계좌 오류 | 미등록 계좌, 예금주 불일치 | 계좌 정보 재확인 유도 |
| 한도 초과 | 일/월 이체 한도 초과 | 현재 한도 수치와 함께 안내 |
| 시스템 오류 | 외부 응답 지연, 타임아웃 | 재시도 가능 여부 명시 |
| 은행 코드 미매핑 | 신규 은행 추가 직후 등 | 폴백 메시지 + 운영 알림 발송 |
분류 기준을 잡고 나니 받는 쪽에서 바로 액션을 결정할 수 있게 됐다. 계좌 오류면 사용자한테 재확인 요청을 보내면 되고, 시스템 오류면 재시도를 안내하면 된다. 코드만 보고 추측하던 시간이 줄었다는 피드백을 운영팀에서 먼저 줬음.
한 가지 놓치지 말아야 할 케이스가 '매핑 안 된 코드'다. 새 은행을 붙이다 보면 문서에 없던 에러코드가 실제 운영에서 나올 수 있다. 이걸 조용히 폴백 처리만 하면 원인 파악이 늦어지니까, 매핑 미스 발생 시 운영 알림이 추가로 나가도록 처리했다.
돌아보면
헷갈렸던 포인트를 솔직하게 적으면 세 가지다.
- 핸들러 등록 키 네이밍 컨벤션이 통일이 안 돼 있었다. 어떤 건 은행 코드 그대로, 어떤 건 내부 별칭을 쓰고 있어서 신규 키를 어떤 규칙으로 넣어야 할지 레지스트리 코드를 한참 봐야 했음.
- 동일 전화번호 중복 파트너 케이스가 운영 데이터에 생각보다 많았다. 테스트 환경에선 안 보이던 케이스라 배포 전에 확인 안 했으면 그냥 지나쳤을 것.
- 실패 코드가 은행마다 의미가 미묘하게 달랐다. A 은행에서 한도 초과를 뜻하는 코드가 B 은행에서는 시스템 점검 코드인 경우가 있어서, 은행별로 코드표를 별도로 관리해야 했다.
다음에 은행 핸들러를 또 붙일 때는 두 가지를 미리 잡아두면 시행착오가 줄 것 같다.
하나는 인터페이스 계약을 더 촘촘하게 유지하는 것. 이번에 변환 메서드를 인터페이스로 올렸는데, 응답 파싱에서 허용하는 입력 포맷의 범위까지 타입 수준에서 잡아두면 구현 편차가 더 줄어든다.
다른 하나는 매핑 테이블을 코드 안에 박지 말고 외부 설정으로 빼두는 것이다. 은행별 에러코드 체계는 공지 없이 바뀌는 경우가 있는데, 그때마다 배포 사이클을 타는 게 부담스럽다. 운영팀이 직접 수정하고 반영할 수 있는 구조가 되면 대응 속도가 달라진다. 이번엔 코드 안에 일단 정의해뒀지만, 다음 iteration에서 설정 파일로 이관하는 게 남아있다.
댓글 0
첫 댓글 달아줘.