결제 잔액 복원 분기에서 텔레그램 알림 직접 호출 제거
목차
결제 출금 흐름에서 잔액 복원 실패 분기에 박혀 있던 텔레그램 알림 호출을 제거했다.
왜 이 코드가 거기 있었나
출금 처리 중 예외가 발생하면 차감됐던 charge_balance를 다시 복원하는 분기가 있다. 일종의 보상 트랜잭션 구간이다. 초기에 이 복원 로직을 짤 때 "복원마저 실패하면 우리가 즉시 알아야 한다"는 판단 하에 텔레그램 알림을 직접 호출하는 코드를 넣었을 거다. 당시엔 합리적인 선택이었다.
문제는 알림 채널이 비즈니스 로직 한복판에 직접 묻혀 있다는 것이다. pay/web 쪽 내부 클래스와 payment/web 쪽 내부 클래스, 두 군데에 동일한 패턴으로 텔레그램 호출이 박혀 있었다. 비슷한 흐름이 두 레이어에 나뉘어 있는 구조 자체도 정리가 필요한 신호였지만, 우선 이번엔 알림 호출 제거에 집중했다.
무엇이 문제였나
| 문제 | 설명 |
|---|---|
| 알림 채널 직접 의존 | 비즈니스 로직이 텔레그램 SDK/클라이언트에 직접 결합 |
| 복원 실패 처리 흐름 복잡화 | 알림 호출 자체가 예외를 던질 경우 복원 분기 흐름이 오염될 가능성 |
| 중복 호출 위험 | 동일 이벤트에 두 클래스에서 각각 알림이 발송될 수 있는 구조 |
| 테스트 어려움 | 알림 클라이언트 mock 없이는 해당 분기 단위 테스트가 번거로움 |
복원 실패 분기는 이미 예외 상황이다. 그 안에서 또 외부 I/O(텔레그램 API 호출)를 직접 치는 건 "예외 처리 중 또 다른 예외 가능성"을 열어두는 셈이다. 텔레그램 쪽이 일시적으로 응답이 느리거나 장애가 나면, 복원 분기 자체가 엉키는 상황이 발생할 수 있었다.
// 기존 패턴 (제거 전)
try {
restoreChargeBalance(userId, amount);
} catch (Exception e) {
log.error("charge_balance 복원 실패", e);
telegramClient.sendAlert("복원 실패: " + e.getMessage()); // ← 이게 문제
throw e;
}
// 변경 후
try {
restoreChargeBalance(userId, amount);
} catch (Exception e) {
log.error("charge_balance 복원 실패", e);
throw e;
}
알림은 이 레이어에서 직접 쏠 게 아니라, 호출 스택 상위에서 또는 별도 이벤트/모니터링 파이프라인에서 처리하는 게 맞다.
리팩터링 판단 기준
이런 류의 "알림 호출 제거"는 얼핏 기능 약화처럼 보여서 팀 내에서 컨펌을 받을 때 설명이 필요하다. 내가 신경 쓴 포인트는 세 가지였다.
- 알림이 사라지는 게 아니다 — 로그는 그대로 남고, 상위 레이어나 인프라 레벨 모니터링으로 탐지 가능하다
- 이 코드가 두 클래스에 중복돼 있던 이상, 한쪽만 수정하면 불일치가 생긴다. 두 군데 동시에 제거하는 게 맞다
- 복원 실패는 DB 레벨 이슈일 가능성이 높아서, 알림보다 트랜잭션 롤백/재시도 전략이 더 실질적인 대응이다
팀원한테 리뷰 줄 때 "알림 제거니까 위험하지 않아?"라는 질문이 나올 수 있다. 그 질문 자체는 맞는 감각이다. 단 "이 코드가 없어지면 모니터링 사각지대가 생기는가"를 정확히 따져봐야 한다. 이번 경우엔 로그와 상위 에러 핸들링이 이미 있었기 때문에 사각지대가 아니었다.
코드리뷰에서 이런 판단 근거를 PR 설명에 명시적으로 적어두는 게 중요하다. "그냥 지웠음"으로 올라오는 순간 리뷰어 입장에서 불안해지고, 불필요한 핑퐁이 생긴다.
pay와 payment 두 패키지가 비슷한 역할로 병존하는 구조는 다음 정리 대상이다. 이번 작업이 그쪽 실마리를 건드린 셈이기도 하다.
끝.
댓글 0
첫 댓글 달아줘.