결제 시스템을 어떻게 디자인할 것인가

죠랭이·2024년 10월 27일
1

아키텍처

목록 보기
3/4

이번에 이직을 시도하면서 무려 두 회사에서 1차 면접 때 주문/결제 시스템 디자인에 대한 질문을 받았다. 처음 받았을 적에는 내가 대답하지 못했던 부분에 대해서만 깊게 학습하고 넘어갔는데 두 번째 회사에서 더 깊게 물어보니 이번 기회에 제대로 정리하고 넘어가면 좋을 것으로 생각이 들었다. 그래서, 내 나름대로 결제 시스템 요구사항과 설계를 다음과 같이 해보았다.(혹시 현업에서 해당 도메인을 개발하고 운영하고 계신 분이 있다면 코멘트 남겨주시면 너무너무 감사하겠습니다.)


어떤 결제 시스템을 만들 것인가?

이 부분은 사실 협업하는 동료들과 이야기를 나누며 요구사항을 정리하면 좋겠으나 일단 필자가 생각하는 결제 시스템에 가장 필요한 요구사항은 다음과 같다고 생각한다.

  • 구매자에게 대금 결제(취소)하기
  • 판매자에게 대금 지급(취소)하기

여기서, 주목해야할 부분은 바로 비기능적 요구사항이다. 필자가 설정한 비기능적 요구사항은 다음과 같다.

  • 한정판 상품 구매 이벤트에도 대응 가능한 시스템(최대 1천만 TPS)
  • 결제수단으로는 체크/신용카드, 페이팔, 알리페이 지원
  • 결제승인/취소 시 latency 1초 이내
  • 24시간 결제(취소) 가능
  • 전 세계 지원. 단, 달러만 제공.

위의 비기능적 요구사항을 요약하면 가용성과 일관성 두 마리의 토끼를 다 잡아야 하는 상황이다. 상세 아키텍처를 그리기 전 Restful API 및 데이터 모델링을 먼저 해보자.

Restful API

결제 시스템에서 필요한 사용자 API는 다음과 같다.

  • 결제하기
POST /v1/payments
{
  "user-id": "user1",
  "user-name": "james",
  "payment-method": "paypal",
  // 결제 방법이 신용/체크카드일 때에만 요청 필드로 활용
  "card-info" : {
    ...
  }
  "products": {
    ...
  }
  "amount" : 10000L,
  "currency" : "USD",
  ...
}
  • 결제 단건 조회
GET /v1/{payment-id}/payment
  • 결제 다건 조회
GET /v1/{user-id}/payments

데이터 모델

결제 시스템에서 필요한 핵심 데이터 모델은 결제와 주문이다. 각각 데이터 모델링을 하면 다음과 같다. 여기서 알게된 사실이 복식부기 회계 시스템이라고 하여 결제의 원자성을 보장하는 기법이 있다고 한다. 자세한 내용은 Square사의 기술 블로그를 참고하기 바란다.

결제시스템_데이터모델

아키텍처

이제 결제 시스템의 상세 아키텍처를 그려보자. 최종적으로 필자가 생각했을 때 기능적, 비기능적 요구사항을 만족하는 결제 시스템은 다음과 같다.

결제시스템_아키텍처

설계 시 고려한 사항으로는 아래와 같다.

  • 결제와 주문 데이터 정합성 핸들링(결제가 성공하면 주문에도 성공, 결제가 실패하면 주문도 실패)
  • Spike 트래픽을 대응하기 위한 비동기 처리
  • 결제 실패에 대한 재처리
  • MSA 분산 트랜잭션을 위한 Saga 패턴

사실 트래픽이 1천만 TPS처럼 대용량이 아니라고 한다면 MSA 구조 및 비동기 처리대신 Database 분산 Lock을 활용하여 Consistency와 Availability를 가져갈 것이다.(참고로, MySQL에서는 Named Lock이라고 하는 분산락 기능을 제공한다.) 하지만, 분산락으로는 PG사 연동 등 외부 시스템 연결에서 대용량 트래픽이 몰렸을 때 가용성을 보장하는 데 한계가 있어 Eventual Consistency를 위한 이벤트 기반 비동기 처리 방식을 선택하였다. 해당 시스템에선 PG사에서 요청에 대한 응답값을 메시지 큐로 전송 가능하다는 전제로 만약 이것이 어렵다면 Payment서버에서 메시지를 받아 큐로 전송해주면 된다. 또한, PG사 시스템 장애로 처리 지연이 발생할 경우 Payment서버 Scale-out으로 외부 시스템 지연에 대응한다. Payment 서버와 PG사 시스템 사이에 메시지 큐를 두어 PG사가 처리 가능한 메시지 수만 polling해가도록 개선하는 것도 하나의 방법일 수 있다. 마지막으로, 결제 실패에 대한 재처리를 위하여 재처리 시도 메시지 큐를 두어 Payment 서버에서 PG사에 결제 재시도를 할 수 있도록 하였다. 이때, 메시지 큐 솔루션으로는 대용량 이벤트 스트리밍 polling 방식을 지원하는 Kafka를 추천한다. RabbitMQ, AWS SQS 등의 전통적인 메시지 큐는 PUSH 방식으로 메시지 브로커 역할을 하기에 이에 해당하는 아키텍처는 지금과는 다르게 적용해줄 필요가 있다.

이와 같이 결제 시스템 디자인을 해보았다. 여기서 더 상세하게 들어간다면 결제 서버와 PG사의 연동 방식, 비즈니스 도메인 지식 등을 확장해갈 수 있다. 특히, 배송과 정산 도메인까지 여기에 추가된다면 해당 시스템 아키텍처는 더욱 복잡해지고 동시성 처리를 주의깊게 다뤄야한다. 혹시라도 해당 시스템 디자인 부분에서 피드백 주고자 하는 부분이 있다면 댓글에 남겨주길 바란다.(필자는 결제 도메인을 실무에서 아쉽게도 해본 경험이 없다ㅠ)긴 글 읽어주어 고맙고 다음 시간엔 새로운 시스템 디자인을 가져와보도록 하겠다.

참고자료

profile
슈퍼 개발자를 목표로 하는 주니어

0개의 댓글