[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인

안녕하세요!
K-DEVCON (이하 “데브콘”) 그로스 매니저 박종훈입니다.

어느새 곧 3월도 끝나가고 있네요.

이번 5주차 스터디에서는 “결제 시스템” 의 디자인에 대해서 함께 이야기를 나눠보았습니다.

---

결제는 비즈니스의 중요한 부분입니다. 돈과 직접적으로 연결되기 때문에, 안정적인 시스템 유지가 필수입니다. 그리고 사용자가 급증하였을 때도 감당할 수 있도록 확장 가능하며, 유연한 시스템 디자인이 필요합니다.

결제시스템 이라고 하면 사람마다 다양한 범위를 고려할 것입니다.
이번 장에서는 전자상거래(이커머스) 를 위한 백엔드를 구축한다고 가정하고 이야기를 논의합니다.

결제의 실질적인 부분은 Paypal, Stripe 와 같은 PSP(결제 서비스 공급자)를 사용한다고 가정합니다.  이러한 외부 결제 서비스와의 통신 과정에서는 네트워크 장애, 타임아웃, API 오류, 일시적인 서비스 중단 등 다양한 이유로 요청이 실패할 수 있습니다. 따라서 시스템은 이러한 실패를 전제로 설계되어야 하며, 재시도 로직, 멱등성 보장, 상태 동기화 등의 대응 전략이 반드시 필요합니다.

실제 서비스에서도 결제 과정에서 간헐적으로 실패를 경험한 적이 있을 것입니다. 이는 외부 시스템과의 연동에서 완전히 피할 수 없는 특성이며, 이러한 불확실성을 어떻게 제어하고 안정성을 확보할 것인지가 중요한 과제가 됩니다. 이번 논의에서는 보다 안정적인 결제 시스템을 구축하기 위한 다양한 고려사항을 다루어 보았습니다.

[0] 배경지식


PSP (Payment Service Provider)
한국에서는 PG(Payment Gateway) 라는 표현을 많이 쓰지만, 엄밀한 의미의 PG 는 말 그대로 데이터 전달이라는 기술적 게이트웨이에 가깝고, 실제적으로 결제 전반의 서비스를 포괄적으로 제공하는 공급자를 PSP 라고 한다고 합니다. 여러 결제 수단을 통합하여 제공해주고, 정산, 리스크 관리(FDS) 등을 제공해줍니다. 대표적인 사례로는 PayPal, Stripe, Adyen 등 이 이에 해당되며, 국내 기준으로는 Toss Payments, NHN KCP, KG Inicis 등이 이에 해당됩니다.

Pay-in 과 Pay-out
이러한 시스템에서는 보통 두가지 자금의 흐름이 발생됩니다.
두 가지 모두 정확성이 중요한 작업입니다.

[1] 안정적인 비동기 결제 시스템 만들기


결제 시스템은 PSP, 은행, 카드사 등 외부 시스템과의 통신이 필수적입니다. 이 과정에서 응답 지연이나 일시적인 장애가 언제든 발생할 수 있기 때문에, 클라이언트에게 빠른 응답을 보장하면서도 뒤에서 안정적으로 처리를 완료할 수 있는 비동기 기반의 설계가 필요합니다.


지갑과 원장

[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인

지갑(wallet)은 상태를 저장합니다. 원장(ledger)은 결제 트랜잭션에 대한 금융 기록입니다. 지갑의 상태가 구성되기 까지의 모든 히스토리들을 기록합니다.
이를 통해, 시스템 오류나 데이터 변조가 발생되었을 경우, 이를 감지할 수 있습니다.


재시도 큐(retry queue) 와 실패 메시지 큐(DLQ, dead letter queue)

비동기 통신에 큐를 사용하면 서비스간의 결합도를 낮출 수 있습니다. 하지만 큐를 사용할 경우, 실패가 발생되었을 때, 어떻게 할지 전략을 구성해 두는 것이 중요합니다.

[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인 - 이미지 2


실패 시 시스템을 중단하고 수동 개입으로 복구할 수도 있습니다. 하지만 이는 사용자에게 좋지 않은 경험을 줄 것입니다.

재시도 큐를 도입하여 재시도를 하게 할 수도 있습니다. 일시적인 문제라면 이를 통해 해결될 것입니다. 

재시도를 할 때는, 같은 동작이 여러번 수행되지 않도록 방지할 수 있는 수단이 필요합니다. 보통 멱등 키를 활용하여 이를 제어합니다. 같은 멱등키로 응답이 들어왔을 경우에는 같은 응답을 반환하는 것을 보장합니다.
 
하지만 만약 재시도를 해도 해결되지 않는 문제라면 어떻게 해야할까요? 이를 위해 실패 메시지 큐가 사용됩니다. 이를 통해 잘못된 데이터(Poison Pill)가 시스템을 마비시키는 것을 방지할 수 있습니다.

그런데 꼭 DLQ 가 필요할까요? 원천데이터가 잘 보관되어 있다면, 별도의 배치나 스크립트를 통해 다시 큐에 밀어넣을 수 있을 것입니다. 하지만 이러한 접근은 구독자(consumer)가 하나일 경우에는 문제가 없을지 몰라도, 여럿일 경우에는 문제가 발생될 수 있습니다. DLQ는 구독자 서비스가 스스로 문제를 격리하여 해결할 수 있는 구조를 만들어 줍니다.


결과 데이터 검증

결제 시스템들은 실제로 결제가 되었는지를 가능한 빠르게, 그리고 정확하게 판단하여 고객한테 알려주고자 노력합니다. 실패는 다양한 원인으로 발생될 수 있기 때문에, 이를 고려해야합니다.

[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인 - 이미지 3
책에서 참고 자료로 링크해준 우버 사례를 참고해보자면 다음과 같이 별도의 검증기(validator) 를 구현하였다고 합니다.
이벤트가 정상적으로 수행되었을 때 나와야 하는 모든 부수 효과(side-effect)가 정상적으로 발생되었는지 확인하고, 실패가 발생되었을 경우에는 경고(alerts)를 보내줍니다.


대사와 조정

[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인 - 이미지 4

대사는 서로 다른 두 개의 데이터 세트를 비교하여 내용이 일치하는지 확인하는 과정입니다. 일반적으로 외부 시스템과의 데이터 정합성을 맞추기 위해서 사용됩니다.

만약 데이터가 일치하지 않을 경우는 그 차이를 해소하기 위해 조정이 필요합니다.
조정은 자동화 할 수 있는 부분은 자동화 할수도 있겠지만, 이를 위한 비용이 높다면, 재무팀을 통해 수동으로 조정을 하기도 합니다.

조정이 마무리 되면 최종적으로 확정(settlement)합니다.

[마무리]

이번 시스템의 목표 TPS는 높지 않았습니다.
결제 시스템은 한 번의 실패로도 매출과 신뢰를 동시에 잃을 수 있는 영역입니다. 따라서 어떻게 하면 안정적인 결제 시스템을 만들수 있을지에 대한 부분이 이번 장의 핵심이었습니다.

실제로 결제 시스템이 포함된 서비스를 운영해본다면 정말 다양한 사유로 결제에 실패하는 것을 볼 수 있을 것입니다.
결제 시스템은 단순한 기능이 아니라, 장애를 견디는 구조를 설계하는 문제였습니다.

스터디에서 실제로 경험했던 이야기들을 포함하여 더 많은 이야기들을 함께 나눠보았지만, 리뷰글은 여기서 마무리 해보겠습니다.

우리 스터디에서는 실제 사례들을 통해 문제를 어떻게 해결해 나갔을까 많은 고민을 해보고 이야기를 나눠보고 있습니다. 이번주도 다들 열심히 참여해주셔서 더 풍성한 스터디가 될 수 있었습니다.

[Review] 시스템 디자인 스터디3 - 5주차 후기 - 결제 시스템 디자인 - 이미지 5


직접 스터디를 개설해보고 싶은 분이 계시다면, K-DEVCON에서 운영을  도와드리겠습니다. 데브콘의 '랩짱'에 도전하여 커뮤니티 성장을 함께 이끌어주세요! K-DEVCON Linkedin 에 DM 부탁드리겠습니다😉