결제대행사 회원 동기화를 비동기 큐로 전환해 정산 누락 해소
목차
이커머스 결제대행사 연동에서 회원 식별 값을 어떻게 관리하느냐는 생각보다 훨씬 자주 골치를 준다. 결제 자체는 성공했는데 나중에 정산·환불 매칭이 안 되는 케이스, 거의 다 초기 설계에서 회원 식별 값을 "있으면 좋고 없어도 일단 돌아가게" 끼워 넣어서 생긴다.
이번 건도 그랬음. 신규 가입 시 결제대행사에 회원 식별 값을 넘기지 않고, 결제 시점에 임시로 붙이는 방식으로 운영하다 보니 매칭 누락이 가끔 터졌다는 운영팀 피드백이 왔음. 정확히는 "가끔"이 아니라 몰아서 터지는 패턴이었을 텐데, 환불·정산 처리가 몰리는 시기에 그 건들이 한꺼번에 드러나는 구조였을 거다.
운영팀 요구사항을 정리하면 단순했다. 가입·수정 이벤트가 생기는 시점에 결제대행사 쪽에도 식별 값을 전파하고, 그 결과를 운영이 직접 확인할 수 있어야 한다는 것.
구조를 어떻게 잡았나
먼저 데이터 모델부터 손댔음. 회원 DTO에 결제대행사 매핑 ID 필드를 공식 필드로 올렸다. 기존엔 결제 요청 시 조합해서 만드는 형태였는데, 그러면 이 ID 값의 생애주기가 회원 도메인이 아닌 결제 도메인에 묻혀 버린다. 회원 생성·수정이 발생하면 변경 이벤트를 외부로 발행하고, 동기화 워커가 비동기로 결제대행사 API를 호출하는 구조로 전환했음.
[회원 가입/수정]
│
▼
[이벤트 발행]
│
├──▶ [동기화 워커] ──▶ [결제대행사 API]
│ │
│ 실패 시 ▼
│ [지수 백오프 재시도 큐]
│
└── [결제 요청 직전]
│
미동기화 체크
│
즉시 fallback 호출
재시도 큐는 지수 백오프로 설계했음. 결제대행사 API가 일시적으로 느리거나 타임아웃 나는 경우를 이미 겪어봤기 때문에, 즉시 재시도는 바로 포기했다. 재시도 간격을 점점 벌리면서 최대 N회 초과 시 운영 대시보드에 미동기화 회원으로 노출되도록 했음.
운영 대시보드 노출이 처음엔 "굳이 필요한가?" 싶었는데, 있고 없고 차이가 컸다. CS 인입 시 "이 회원 동기화 됐나요?"를 개발자한테 물어보는 루프 자체가 사라짐.
동기 호출을 먼저 짰다가 뒤집은 이유
첫 설계는 가입 트랜잭션 안에서 결제대행사 API를 동기로 호출하는 방식이었음. 가장 단순하고 일관성이 명확하기 때문에 "일단 이렇게 짜고 문제 생기면 바꾸자"는 판단이었다.
문제는 예상보다 빨리 왔다. 결제대행사 API 응답이 일시적으로 느려지는 시간대에 가입 요청이 몇십 개 쌓이면서 가입 자체가 타임아웃 나는 상황 직전까지 갔음. 외부 API를 동기 흐름에 묶으면 그 API의 SLA가 곧 우리 서비스 SLA가 된다는 걸 다시 한 번 체감했다.
비동기 + 큐로 갈아엎었더니 이번엔 다른 구멍이 생겼음. 가입 직후 바로 결제를 시도하는 케이스에서 동기화가 아직 안 된 상태로 결제대행사 API를 호출하게 되는 상황.
| 케이스 | 처음 설계 | 수정 후 |
|---|---|---|
| 가입 직후 동기화 | 동기 블로킹 | 비동기 이벤트 |
| 결제 시점 미동기화 | 미고려 | 결제 직전 보장 호출 |
| 재시도 | 없음 | 지수 백오프 큐 |
| 운영 가시성 | 로그뿐 | 미동기화 목록 대시보드 |
이 구멍을 막은 건 결제 직전에 동기화 여부를 체크하고, 안 됐으면 즉시 fallback 호출을 넣는 것으로 해결했음. 큐가 밀리든 워커가 느리든, 결제 요청이 들어오는 시점에는 자기 책임 하에 강제 동기화하는 구조. 이 라인 하나가 "가입 직후 결제" 시나리오를 커버했다.
비동기 설계의 본질적인 트레이드오프는 "지연"을 "일관성 구멍"으로 치환한다는 것. 그 구멍이 어디에 생기는지 시나리오별로 명시적으로 그려보지 않으면 결국 운영 중에 발견하게 된다. 이번엔 개발 단계에서 "가입 직후 결제"를 시뮬레이션해봤기 때문에 배포 전에 잡았음. 공짜가 아니다.
이번에 정리된 것들
외부 결제대행사 API 호출은 가입·수정 트랜잭션 동기 흐름에 절대 묶지 않는다는 건 이제 팀 내 암묵적 원칙이 됐음. 한 번 타임아웃 직전 상황을 겪으면 다음 설계부터 본능적으로 큐부터 그리게 된다.
비동기로 뺄 때 습관적으로 물어봐야 할 질문이 있다.
- 이 동기화가 완료되기 전에 사용자가 다음 액션을 취할 수 있는가?
- 그 다음 액션이 동기화 결과를 전제로 하는가?
- 그렇다면 그 액션 시점에 fallback이 있는가?
"큐 지연 + 즉시 결제" 조합은 이커머스에서 거의 항상 발생한다고 봐야 함. 가입 완료 직후 장바구니 결제로 이어지는 UX 자체가 흔하기 때문에, 이 시나리오를 예외로 두는 설계는 처음부터 구멍을 열어두는 것과 같다.
운영팀이 동기화 현황을 직접 볼 수 있게 만든 게 예상보다 효과가 좋았음. 미동기화 목록이 대시보드에 뜨면 운영팀이 패턴을 본다. "이 시간대에 유독 많네" 같은 인사이트를 운영팀이 먼저 가져오기 시작하면, 개발팀이 CS 인입 후 로그 뒤지는 루프에서 벗어날 수 있다. 데이터는 결국 사람이 봐야 신뢰됨.
운영팀 피드백 → 스펙 정리 → 사고 시뮬레이션 → 설계 순서로 한 바퀴 더 도는 게 재작업 비용보다 싸다는 것도 다시 확인했음. 피드백 받자마자 바로 개발로 들어가지 않고 "이 변경이 생기면 어떤 시나리오에서 무엇이 깨지나"를 먼저 그리는 습관이 이번에도 재작업 하나를 아꼈다.
댓글 0
첫 댓글 달아줘.