새소식

Java

[Spring] 결제시스템, Spring Boot + MSA 로 리팩토링 [기획 & 설계]

  • -
728x90
실무에서 결제 프로세스를 다루며 많은 문제점들이 발견했다. 레거시한 코드와 절차지향의 프로세스를 통해 처리를 하다 보니 성능이 떨어졌으며, 약간의 과부하만 발생해도 지연 문제가 빈번하게 발생했다. 또한 컴포넌트 간의 결합이 너무 나도 강했으며 모든 관리를 주문 프로세스에서 처리해서 가독성과 유지보수성 모두 크게 떨어졌다. 이러한 아쉬운 점들을 개선하며 Java 와 Spring Boot 로 재구현, 비동기적으로 결제 프로세스를 처리하고 각 도메인 별로 서비스를 분리한 MSA 구조의 결제 서비스를 구현하는 프로젝트에 대한 내용을 담아보려고 한다.

 

🟢 결제시스템, Spring Boot + MSA 로 리팩토링

이번 프로젝트는 위에 설명한 것처럼 비동기적인 처리 + MSA 구조의 결제 시스템을 구현할 것이다. 나의 계획은

 

1. 각 도메인 별로 서비스를 구현한 후에 이를 동기적으로 처리하는 서비스.

2. Event 를 이용한 리팩토링

 

위와 같은 방식을 기반으로 점진적 리팩토링을 진행할 계획이다. 이제 요구사항을 분석하고 프로젝트의 세부 기능을 설계해 보자!

 

📃 요구사항

내가 생각하는 요구사항은 온라인으로 상품을 등록하고 판매하는 e-commerce 시스템이며, 다량의 데이터와 높은 동시 접속자 수 환경에서 안정적인 요청을 처리할 수 있어야 한다는 것이다. 이를 기반으로 정리해보면

  • 판매자는 상품을 등록할 수 있다.
  • 판매자는 상품의 설명, 수량 등의 정보를 관리할 수 있다.
  • 구매자는 상품을 구매할 수 있다.
  • 구매자는 상품을 검색할 수 있다.
  • 구매자는 결제수단을 등록하고 주문 시 사용할 수 있다.
  • 구매자는 배송지를 등록하고 주문 시 사용할 수 있다.
  • 구매자는 주문 내역 리스트 + 배송 상태를 볼 수 있다.
  • 구매자는 회원으로 등록 + 로그인 할 수 있다.

기본적인 요구사항은 위와 같다. 그렇다면 기술적인 요구사항에서, 대용량 트래픽에 어떻게 대처해야 할까 ?

 

대용량 트래픽 대처 전략

대용량 트래픽에 대응하는 방안으로는 서비스의 확장이 제일 대중적일 것이다. 확장성(Scalability) 이란 서비스가 더 많은 작업 부하를 수용하도록 대처할 수 있는 능력이다.

확장성이란

 

만약 초당 3,000개의 요청을 처리해야 하는 서비스에서 요청량이 늘어날 경우, 처리할 수 있는 방법은 무엇일까 생각해보면 수직/수평 확장이 있다. 이에 대해 정리해보면 .. 

 

수직 확장, Vertical Scaling

  • 개별 서버의 업그레이드로 성능 향상 (CPU, RAM, DISK 등)
  • 확장시 일시적 중단 필요
  • 하드웨어 비용이 높고, 금방 한계에 도달

수평 확장, Horizontal Scaling

  • 서버를 추가하여 처리 능력을 확장
  • 일시적 중단이 필요없고 리소스 추가/제거에 유연
  • 이론상 무제한 추가 가능
  • 하드웨어 비용 저렴
  • 복잡성이 증가하며 데이터 일관성 문제가 발생할 수 있음.

이렇게 정리할 수 있다. 하지만 DB 를 확장하는 경우, 일관성을 유지할 수 있을 지 고민해 보아야 한다. 주로 수평 확장을 고려하지만, 관계형 데이터베이스 서버(RDB Server) 의 경우엔 수직 확장을 더 많이 고려하는 것 같다. 반대로 NoSQL 데이터베이스 서버는 수평 확장을 많이 고려한다.

 

확장성을 위한 복제와 분산

수평 확장을 위한 방법에는 아래의 두가지 방법이 있다.

 

복제(Replication)

데이터나 서비스의 복사본을 만들어서 처리한다.

 

 

Primary 와 Replica 가 존재

Replica 는 읽기만 가능

읽기 성능을 분산 가능, 쓰기 부하는 분산이 불가능

복제 지연의 문제(데이터 일관성)

Replica 를 장애 복구용으로 사용 가능

 

 

분산(Distribution)

 

데이터나 서비스를 여러 곳에 나누어서 처리한다. 예를 들어 노드 별로 처리할 범위를 지정해서 처리할 수 있다(EX, 사용자 ID 기준)

group 1, 1~1000

group 2, 1001~2000

group 3, 2001~3000

 

 

여러 노드가 범위를 나누어 데이터를 처리

RDB 의 파티셔닝(샤딩), Redis 클러스터 등이 있음

읽기&쓰기 부하를 분산할 수 있어서 성능 향상에 용이

밸런싱과 Routing 이 중요하고, consistent hashing 과 같은 기법이 사용됨

RDB 에서는 JOIN 연산이 불가능

 

 

 

분산으로 해결되지 않는 문제가 몇가지 있다. 먼저, 분산 처리에 따라오는 복잡도와 동시성 문제이다. 이는 데이터 일관성의 문제와 Routing 의 처리, 노드 개수 변화에 따른 재조정, 즉, rebalancing 등이 문제가 될 수 있다. 또한 요청량이 급변할 때의 확장 문제, Spike traffic 이 발생했을 경우 분산으로는 서비스가 정상적으로 운영되지 않을 수도 있다. 마지막으로 수용할 수 있는 처리량을 넘었을 때 일시적인 오류도 발생할 수 있다. ⭐️ 그렇기에 이 모든 것을 해결하기 위한 방안으로 Event-Driven 아키텍처를 선택했다!

 

Event-Driven Architecture

EDA 는 이벤트(메세지) 기반으로 비동기 통신을 이용해 상호작용하는 구조이다. 이벤트 기반 처리로 얻을 수 있는 것들은 아래와 같다.

 

1. 요청량 변화에 탄력적으로 대응하며, 비동기적 특성으로 쉬운 확장이 가능하다.

2. 관련 서비스의 일시적 오류에도 데이터 유실없이 정상 작동할 수 있다.

3. 다른 서비스들과의 느슨한 결합, 변경 영향도도 최소화할 수 있다.

4. 대용량 데이터의 실시간 처리가 가능하다.(Batch 처리와 비교했을 때 큰 강점)

 

이러한 특징 중, 독립된 서비스가 비동기로 통신하는 느슨한 결합 구조인 MSA 구조로 이번 프로젝트를 설계해 볼 계획이다.

 

📚 기획 및 설계 

 

Lucid visual collaboration suite: Log in

Log in to access the Lucid Visual Collaboration Suite

lucid.app

Catalog → 상품 정보 관리
Payment → 결제 처리와 관련된 작업
Order → 주문 처리를 수행하고 상태를 관리
Delivery → 주문 완료된 제품 배송, 상태 관리
Member → 회원 등록 관리와 인증

 

lucid 를 사용해서 위에 설계한 요구사항대로 Grouping 을 진행해 봤다. 크게 5가지 도메인으로, 모두 별개의 서비스로 구현할 계획이다.

5개의 서비스 Grouping

 

이제 주문에 대한 플로우를 정리해 보았다. 사용한 툴도 너무 신기했고, 궁금해 하는 사람이 있을 수도 있어서 첨부한다!

 

Online FlowChart & Diagrams Editor - Mermaid Live Editor

 

mermaid.live

시퀀스 다이어그램

 

💡 아키텍처 설계 시 고려사항

대용량 트래픽 처리 → 동시에 많은 요청을 처리

탄력성 → 급증하는 트래픽 처리

안정성 → 트래픽 급증 또는 일부 장애 시 에러 최소화

 

내가 고려한 사항은 이렇게 3가지이며, 이를 모두 고려하며 100만명 동시 접속자 처리가 가능한 아키텍처를 설계해 보려고 한다. 100만명이 10분에 한번씩 구매하면 이는 초당 1,666건의 구매처리가 가능해야 함을 의미한다. 이를 인지하고 고려해야 할 사항에 대한 대처 전략을 정리해보면 .. 

 

1. 대용량 트래픽 처리

  • 여러 개의 노드(인스턴스) 사용
  • scale-out, 서버를 여러대 추가하여 분산 처리 (일부 데이터에 NoSQL 적용, 이는 RDB 는 JOIN 이 불가능하기 때문!)

2. 탄력성

  • scale-out 을 이용해서 대응, EDA 를 사용해서 보완

3. 안정성

  • EDA 를 사용해 비동기 처리를 함으로 장애 전파 최소화

 

⭐️ 추가로 설명할 부분은 병목 지점과 EDA 적용이 가능한 부분이다.

Catalog-Service의 빈번한 상품 정보 변경에서 병목이 발생할 수 있다. 주문 요청 증가 시, 상품 수량 변경을 위한 요청 또한 증가할 것이기 때문이다. 이를 수평 확장이 간으한 고성능 NoSQL DBMS(Cassandra, Redis) 를 상용해서 로 대처할 것이다.

 

주문 처리시 결제, 배송 관련 외부 시스템 등에서도 병목이 발생할 수 있다. 주문 요청 처리 시, delivery & payment 등 외부 시스템을 호출하며 병목 현상이 발생할 것이다. 메세지 브로커인 Kafka 를 사용해 분산 시스템에서 요청을 비동기로 처리하고, 시스템 컴포넌트간의 디커플링을 지향해서  완화시킬 것이다.

 

⭐️ Cassandra 를 적용한 이유
Cassandra 는 NoSQL 의 Masterless 분산형 아키텍처로 대규모 확장에 유리하다.
노드를 추가함에 따라, 요청 처리 성능이 선형으로 증가하며, master-slave 구조와 다르게, P2P 프로토콜로 작동하며, 단일 장애 지점없이 지속적으로 시스템을 운영할 수 있다.
대용량의 고객 유입을 전제하는 요구사항에 따라, 고가용성의 NoSQL DBMS 인 Cassandra 를 다수의 외부 서비스에서 호출되는 catalog-service 와 연결했다.

 

EDA 적용 가능한 부분들에 있어서 우선 결제 처리는 외부 모듈 연동을 통해야 하는 경우 처리가 오래걸릴 수 있어서 적용이 가능하다.

결제가 가능한지 파악하고, 결제 요청은 비동기로 처리하는 방식을 고안했다. 배송 처리는 배송 요청 시, 응답이 필요 없고 수행해야 하는 동작만 있어서 비동기 처리에 적합하다. 검색에 대한 캐시(색인) 업데이트 또한 약간의 지연이 허용되기 때문에 비동기 처리와 대량 처리가 용이하다. 이 부분은 Catalog 서비스에서 상품 변경 이벤트가 발행됐을 때, 추가의 서비스에서 이벤트를 소비하며 Cache 를 업데이트하는 방식을 추가하고 싶어 SearchService, 검색 서비스를 추가로 설계했다.

 

시스템 다이어그램

 

실무에 모든 프로세스를 담을 수 없어 간략하게 이러한 플로우로 프로젝트를 진행해 보고자 한다. 지금까지 기획과 설계, 의도 등을 기록했다. 다음 글부턴 실제로 서비스를 구현해 나가는 과정을 적어보겠다 ⭐️

728x90
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.