들어가며
안녕하세요?
KT Experience Engineering 본부에서 벡엔드 개발자로 근무하고 있는 이상현 입니다.
저희 팀은 최근 KT에서 소상공인 사장님들을 위한 다양한 서비스를 한 곳에서 제공하는 소상공인 혁신 플랫폼 '사장이지' 앱에 적용되는 인앱 결제 기능을 개발했습니다.
인앱 결제 개발에 있어서 저희 팀의 개발 목표는, 여러 제휴사와 연결된 구조에서도 실패에 안전하고 중복 처리 없는 신뢰성 있는 시스템을 구축하는 것었습니다.
지난 편('사장이지' 인앱 결제 백엔드 개발#1: 결제 서비스 신뢰성 확보를 위한 노력 )에 이어서, 이번 글에서는 인앱 결제 신뢰성 확보를 위한 '분산 환경에서의 데이터 일관성 관리'에 대해 설명 드리겠습니다.
분산 환경에서의 데이터 일관성 관리
문제 상황
버튼 클릭 → 앱스토어 결제 → "결제 완료!" → 서비스 제공 실패 → 앱에는 서비스 활성화로 표시
위 상황이 인앱 결제에서 고려할 수 있는 가장 나쁜 상황 중 하나일 것 같습니다.
드문 일이지만 실제 서비스를 외부 제휴사 서버를 통해 제공하는 ‘AI 매장음악’의 분산 환경의 특성 상 이슈가 발생하면
데이터 일관성 문제가 발생 가능합니다.
| 위험도 |
데이터 일관성 문제 케이스 |
|---|---|
| 높음 | 사장이지 서버 내 구독은 활성화 되었으나 제휴사 연동 실패로 실제 서비스는 사용 불가 |
| 다소 높음 |
제휴사 구독 서비스는 활성화 되었으나 사장이지 앱에서는 구독이 비활성화로 보임 (제휴사에 구독 전파 후 readTimeout 이 발생하였으나 제휴사 내부에서는 활성화 처리 완료) |
표. 데이터 일관성 문제 케이스
일반적인 결제와 인앱 결제 제약 사항
일반적으로 결제하면 데이터 일관성이 중요하기 때문에 분산 환경인 경우에는 보상 트랜잭션과 같은 논리적 롤백을 통해 여러 시스템 간 결제 데이터 일관성을 유지 되도록 노력합니다. (결제 승인, 취소, 갱신 등등)
그러나 인앱 결제의 경우 결제 프로세스의 상당 부분을 앱 마켓에서 관리를 하기 때문에 사장이지 서버와 같이 결제 이벤트에 따라 서비스를 제공 하는 단계에서는 트랜잭션을 되돌리기 어렵습니다. (결제 승인의 경우 구글은 Confirm, 환불 API 제공)
그림. 분산 환경에서의 데이터 일관성 관리
해결 방안 : 구독 서비스 특성과 최종 일관성 선택
일반적으로 인앱 결제에서 다루는 상품의 종류는 크게 3가지 입니다.| 상품 유형 |
사용 패턴 |
예시 | 일관성 요구 사항 |
|---|---|---|---|
| 소모성 상품 | 웹툰 쿠키, 게임 재화 등 | 구매 즉시 소비 | 강한 일관성 필요 |
| 비소모성 상품 |
광고 제거 등 |
구매 후 지속 이용 |
강한 일관성 필요 |
| 구독형 상품 |
유튜브 프리미엄, AI 매장 음악 |
월/연 단위 지속 이용 |
최종 일관성 허용 (Eventual Consistency) 구독 만료 전 약 24시간 전부터 자동 갱신 시도 |
표. 인앱 결제 상품 종류
현재 사장이지에서는 구독형 상품 위주로 인앱 결제를 사용하고 있습니다. 최초 결제의 경우에는 일관성을 고려할만 하지만 그 이후 결제 프로세스들에서는 즉각적인 일관성 보다는 신뢰성이 중요하다고 생각이 되었습니다.
위와 같은 이유로 분산 환경에서 결제 이벤트에 대한 최종 일관성을 목표로 설계를 진행하게 되었습니다.
실패 범위 최소화를 위한 트랜잭션 경계 지정
우선 분산 환경 상에서 실패 범위를 최소화 하고 실패를 컨트롤 하기 위해 트랜잭션 경계를 나누었습니다.
그림. 실패 범위 최소화를 위한 트랜잭션 경계 지정
| 경계 | 트랜잭션 관리 주체 |
설명 |
|---|---|---|
| [트랜잭션 경계 1] 앱 마켓 - 사장이지 서버 (구독 관리) |
구글 플레이/앱스토어 등 앱 마켓 |
사장이지 서버 장애 등으로 결제 이벤트 처리가 불가능한 경우 각 앱 마켓에서 지원하는 결제 이벤트 전송 및 재시도를 활용하여 트랜잭션 관리 |
| [트랜잭션 경계 2] 사장이지 서버 - 제휴사 (서비스 제공) |
사장이지 서버 인앱 결제 모듈 |
사장이지 서버에서 결제 이벤트를 받아서 저장하면 그 이후 전파 트랜잭션 관리 전파 실패 시 비지니스 플로우를 사장이지에서 직접 관리 가능 → Eventual Consistency |
표. 분산 트랜잭션 경계
각 앱 마켓에서는 인앱 결제 백엔드에 결제 이벤트 전파 실패 시 아래 정책으로 재시도를 수행합니다.
트랜잭션 경계 1 은 사장이지 서버의 지연/장애로 인해 결제 이벤트가 정상적으로 처리 될 수 없는 경우에도 앱 마켓에 의해 최종적으로는 일관성을 보장할 수 있습니다.
트랜잭션 경계 1 은 사장이지 서버의 지연/장애로 인해 결제 이벤트가 정상적으로 처리 될 수 없는 경우에도 앱 마켓에 의해 최종적으로는 일관성을 보장할 수 있습니다.
Google Play 의 RTDN (Google Pub/Sub) 재시도 정책: 재시도 요청 | Pub/Sub | Google Cloud Documentation
AppStore 의 Server Notification 재시도 정책 : Responding to App Store Server Notifications | Apple Developer Documentation
제휴사 전파 실패 시 최종 일관성
트랜잭션 경계 2 에 해당하는 부분으로 사장이지 서버의 데이터베이스에는 구독 상태가 결제 이벤트에서 전달한 상태로 반영 된 이후 입니다.이후에는 결제 이벤트를 제휴사에 전파하는데 이때 제휴사의 이슈로 오류를 발생 시키거나 처리는 되었으나 지연이 발생한 경우에는 사장이지 서버 입장에서는 전파 실패로 인식합니다.
사장이지 서버에서는 제휴사 전파 실패 시 최종 일관성을 위해서 아래 방법들을 사용합니다.
1. 구독 처리 상태 관리
2. 재시도
3. 재시도/실패 내역 관리 및 보상
그림. 제휴사 전파 실패 시 최종 일관성
- 구독 처리 상태 관리
제휴사 전파를 실패하는 경우 구독 처리 상태를 관리하기 시작합니다.
결제 이벤트 수신 완료 → 결제 이벤트 처리중
추가로 이 상태로 전환된 구독들은 불일치가 해소되고, 최종일관성이 보장되기 전까지 모니터링 되고 추적 가능한 상태로 관리 되어야 합니다.
- 재시도
구독 처리 상태를 관리하면서 최종 일관성을 보장을 위해 정책에 따라 재시도를 진행합니다. 재시도는 일시적인 지연에 대응할 수 있는 간편하고 좋은 수단입니다. (그림. 제휴사 전파 실패 시 최종 일관성 1,2,3)
일정 수준 이상의 반복 실패하는 경우에는 제휴사의 장애인 경우가 많아 재시도 간격 관련 지연 정책을 다루고 있습니다. 사장이지 서버가 재시도를 진행하는 경우에 제휴사는 동일한 이벤트가 중복 발송 될 수 있기 때문에 멱등성을 고려해야 합니다.
구독 처리 상태를 관리하면서 최종 일관성을 보장을 위해 정책에 따라 재시도를 진행합니다. 재시도는 일시적인 지연에 대응할 수 있는 간편하고 좋은 수단입니다. (그림. 제휴사 전파 실패 시 최종 일관성 1,2,3)
일정 수준 이상의 반복 실패하는 경우에는 제휴사의 장애인 경우가 많아 재시도 간격 관련 지연 정책을 다루고 있습니다. 사장이지 서버가 재시도를 진행하는 경우에 제휴사는 동일한 이벤트가 중복 발송 될 수 있기 때문에 멱등성을 고려해야 합니다.
연동 단계에서 관련 기능을 제휴사에 요청하거나 중복 시 제휴사에서 발생 시키는 중복 에러 응답을 사장이지 서버에서 정상 처리 시키고 있습니다.
위와 같이 재시도를 통해 제휴사 전파가 성공 하는 경우에는 구독 처리 상태를 다시 완료 상태로 변경하지만 최대 재시도 횟수를 초과하는 경우에는 구독 처리 상태를 실패 상태로 변경합니다. 또한 실패 상태는 따로 전파되어 관리가 필요합니다.
그림. 사장이지 내부 결제 이벤트 상태 관리
- 재시도/실패 내역 관리 및 보상
관련 작업을 위해 재시도와 실패 내역은 모니터링 되고 관리자가 알 수 있어야 합니다. 일시적 지연의 경우에는 재시도로 정상화 될 수 있지만 제휴사 장애 등으로 인한 실패는 관리자가 대응할 수 있어야 합니다.
1. 관리자를 통한 수동 재전파
2. 관리자를 통한 보상 처리
관리자가 포탈을 통해서 강제로 다시 구독 상태를 제휴사에 전파 할 수 있으면 제휴사 장애만 복구 되면 정상화 시킬 수 있습니다. (그림. 제휴사 전파 실패 시 최종 일관성 6)
또한 인앱 결제 구독의 경우 각 앱 마켓에서는 위와 같은 상황에 구독을 일단위로 연장 가능한 API를 제공하고 있어 환불 대신에 보상 처리로 구독 연장 등도 가능합니다.
마무리
결제 도메인은 단순한 기능 구현 이상의 고민을 요구하기 때문에 항상 개발자에게 챌린지를 주는 것 같습니다.
이번 인앱 결제 백엔드 개발을 통해 느낀 핵심은, 결제 시스템에서는 신뢰성(Reliability), 일관성(Consistency), 확장성(Scalability), 운영성(Operability) 등 비기능적 설계가 무엇보다 중요하다는 점입니다.
또한 이러한 설계 패턴과 해결 전략은 결제 도메인에 국한되지 않고 멱등성 처리, 분산 트랜잭션 관리, 재시도 등은 향후 다양한 서비스 연계에도 적용할 수 있어 고민하고 개선해 나갈 필요가 있다고 생각합니다.
아직 서비스를 출시한지 1년이 되지 않아 대비한 장애 시나리오들이 발생하지 않은 경우도 존재하지만 전혀 오버엔지니어링이라고 생각하지 않습니다. 인앱 결제 기능 자체는 단순해 보이지만 미처 대비하지 못한 상황들로 인해 장애가 발생하게 되면 사장님들이 겪을 유저 경험과 사장이지에 대한 신뢰도를 생각하면 매우 중요하기 때문입니다.
앞으로도 사장이지를 사용하는 사장님들이 결제할 때 불편함 없이 안정적으로 서비스를 경험할 수 있도록 지속적으로 고민하고 개선할 예정입니다. 이를 기반으로 현재 제공 중인 구독형 서비스뿐 아니라 앞으로 추가될 다양한 서비스들을 안정적으로 제공할 수 있는 환경을 마련하는 것이 목표입니다.