프로젝트 목록으로 돌아가기
Recruiter Briefmsa-shop

MSA Shop

주문·결제·재고를 6개 서비스로 분리하고, SAGA·Outbox와 E2E 검증으로 분산 트랜잭션 복구 경로를 끝까지 확인했습니다.

Role
백엔드·인프라 설계 및 구현
Period
2026
Team
개인 프로젝트
Theme
Infra
Java 21Spring Boot 3Spring Cloud GatewaySpring Data JPASpring SecurityJWTMySQLRabbitMQ

30초 요약

문제
주문·결제·재고가 서비스별로 나뉘면 중간 실패와 보상 흐름을 검증해야 했습니다.
해결
SAGA, Outbox, RabbitMQ 이벤트, Docker Compose, Helm 템플릿으로 분산 흐름을 구성했습니다.
검증
Gateway 기준 E2E 스크립트와 Helm 구성으로 서비스 간 흐름과 배포 템플릿을 검증했습니다.

Problem

Problem

주문, 결제, 재고, 정산이 서비스와 저장소로 분리되면 성공 흐름보다 중간 실패와 보상 흐름을 먼저 설계해야 했습니다.

Decision

Decision

2PC 대신 SAGA와 Outbox를 사용해 즉시 일관성보다 재시도, 보상, 검증 가능한 E2E 흐름을 우선했습니다.

Build

Implementation

  • 재고 예약, 결제, 주문 저장을 SAGA 단계로 나누고 실패 지점별 보상 호출을 정의했습니다.
  • 결제 성공 후 주문 저장 실패처럼 이벤트 유실이 치명적인 구간은 Outbox 테이블과 폴링 프로세서로 분리했습니다.
  • Docker Compose, Helm, GitHub Actions E2E 스크립트를 같이 두어 로컬과 CI에서 같은 흐름을 검증할 수 있게 했습니다.

MSA는 서비스 개수를 늘리는 작업이 아니라 실패를 되돌리는 절차와 자동 검증 경로를 함께 만드는 작업이라는 점을 정리했습니다.

Proof

Verification

docs/IMPLEMENTED-SUMMARY.md와 e2e-all-scenarios.sh, Helm 차트 링크를 기준으로 6개 서비스 구성, Gateway 기준 E2E 스크립트, Helm 배포 템플릿을 확인했습니다.

6개
Designed
서비스

Gateway를 포함한 주문·결제·재고 분산 시스템 구성

E2E
Verified
통합 흐름

Gateway 기준 주문·정산·실패 케이스 검증 스크립트

Helm
Designed
배포 템플릿

Kubernetes 배포 구성을 템플릿으로 문서화

Verified

E2E scripts

분산 흐름 검증

Scenario
인증, 주문, 장바구니, 실패 케이스, 정산 흐름
Method
GitHub Actions에서 서비스 기동 후 E2E 스크립트 실행
Result
Gateway 기준 통합 스크립트로 성공·실패 경로를 검증할 수 있게 구성했습니다.
Designed

Helm chart

배포 구성

Scenario
Kubernetes 배포 템플릿 관리
Method
서비스별 Deployment, Service, ConfigMap, HPA를 Helm으로 분리
Result
로컬 Compose와 Kubernetes 배포 템플릿을 함께 구성했습니다.

Boundaries

Trade-offs & Limitations

Trade-offs

  • SAGA/Outbox는 분산 트랜잭션을 현실적으로 다루지만 즉시 일관성보다 보상과 재처리 흐름이 중요해집니다.
  • 서비스를 6개로 나누면 책임은 명확해지지만 로컬 기동과 테스트 비용이 늘어납니다.

Limitations

  • 실제 PG, 물류, 정산 외부 연동은 모의 도메인으로 대체했습니다.
  • 운영 클러스터가 아닌 로컬·CI 기반 검증 결과입니다.
프로젝트 다이어그램

아키텍처

전체 아키텍처

architecture

전체 아키텍처

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Client

사용자 진입과 실시간 상태 확인

Web / App

Application

API, 도메인 로직, 워커 처리

API Gateway (8080) routing / JWT / Rate Limit / Swagger

user-service (8081) user / login / JWT

Services

order-service (8083) order / cart / SAGA / Outbox

Services

payment-service (8084) payment / cancel

Services

settlement-service (8085) daily / monthly settlement

Services

Services

Data / Messaging

상태 저장, 캐시, 이벤트 전달

product-service (8082) product / stock / cache

Services

RabbitMQ payment.events

Services

userdb

Storage

productdb

Storage

Redis product cache

Storage

orderdb

Storage

paymentdb

Storage

settlementdb

Storage

Ops

관측성, 배포, 운영 보조

Prometheus

Monitoring

Grafana

Monitoring

Zipkin

Monitoring

핵심 연결 흐름
  1. 1. Web / App → API Gateway (8080) routing / JWT / Rate Limit / Swagger · HTTP Bearer JWT
  2. 2. API Gateway (8080) routing / JWT / Rate Limit / Swagger → user-service (8081) user / login / JWT · /users /auth
  3. 3. API Gateway (8080) routing / JWT / Rate Limit / Swagger → product-service (8082) product / stock / cache · /products
  4. 4. API Gateway (8080) routing / JWT / Rate Limit / Swagger → order-service (8083) order / cart / SAGA / Outbox · /orders /cart
  5. 5. API Gateway (8080) routing / JWT / Rate Limit / Swagger → settlement-service (8085) daily / monthly settlement · /settlements
  6. 6. order-service (8083) order / cart / SAGA / Outbox → payment-service (8084) payment / cancel · REST payment / cancel
  7. 7. order-service (8083) order / cart / SAGA / Outbox → product-service (8082) product / stock / cache · REST stock reserve / release
  8. 8. payment-service (8084) payment / cancel → RabbitMQ payment.events · payment.completed event
  9. 9. RabbitMQ payment.events → settlement-service (8085) daily / monthly settlement · subscription
  10. 10. user-service (8081) user / login / JWT → userdb
  11. 11. product-service (8082) product / stock / cache → productdb
  12. 12. product-service (8082) product / stock / cache → Redis product cache
  13. 13. order-service (8083) order / cart / SAGA / Outbox → orderdb
  14. 14. payment-service (8084) payment / cancel → paymentdb
  15. 15. settlement-service (8085) daily / monthly settlement → settlementdb
  16. 16. Services → Zipkin · traces
  17. 17. Services → Prometheus · metrics
  18. 18. Prometheus → Grafana
원본 Mermaid 보기
flowchart TB
    Client["Web / App"]
    Gateway["API Gateway (8080)<br/>routing / JWT / Rate Limit / Swagger"]
    subgraph Monitoring["Monitoring"]
      Prometheus["Prometheus"]
      Grafana["Grafana"]
      Zipkin["Zipkin"]
    end
    subgraph Services["Microservices"]
      User["user-service (8081)<br/>user / login / JWT"]
      Product["product-service (8082)<br/>product / stock / cache"]
      Order["order-service (8083)<br/>order / cart / SAGA / Outbox"]
      Payment["payment-service (8084)<br/>payment / cancel"]
      Settlement["settlement-service (8085)<br/>daily / monthly settlement"]
      Rabbit["RabbitMQ<br/>payment.events"]
    end
    subgraph Storage["Data Storage (independent DB per service)"]
      UserDb[("userdb")]
      ProductDb[("productdb")]
      Redis[("Redis<br/>product cache")]
      OrderDb[("orderdb")]
      PaymentDb[("paymentdb")]
      SettlementDb[("settlementdb")]
    end

    Client -->|HTTP Bearer JWT| Gateway
    Gateway -->|/users /auth| User
    Gateway -->|/products| Product
    Gateway -->|/orders /cart| Order
    Gateway -->|/settlements| Settlement
    Order -->|REST payment / cancel| Payment
    Order -->|REST stock reserve / release| Product
    Payment -->|payment.completed event| Rabbit
    Rabbit -->|subscription| Settlement
    User --> UserDb
    Product --> ProductDb
    Product --> Redis
    Order --> OrderDb
    Payment --> PaymentDb
    Settlement --> SettlementDb
    Services -. traces .-> Zipkin
    Services -. metrics .-> Prometheus
    Prometheus --> Grafana

ERD

전체 ERD

erd

전체 ERD

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

users
bigint
id
PK
string
email
UK
string
password
string
name
products
bigint
id
PK
string
name
string
category
int
price
int
stock_quantity
cart_items
bigint
id
PK
bigint
user_id
FK
bigint
product_id
FK
int
quantity
orders
bigint
id
PK
bigint
user_id
FK
bigint
product_id
FK
int
quantity
int
total_amount
string
status

+ 2개 필드

payments
bigint
id
PK
bigint
user_id
FK
int
amount
string
payment_method
string
status
datetime
created_at
outbox_events
bigint
id
PK
string
event_type
text
payload
string
status
datetime
created_at
datetime
processed_at
daily_settlements
bigint
id
PK
date
report_date
UK
bigint
total_amount
int
payment_count
monthly_settlements
bigint
id
PK
string
settlement_year_month
UK
bigint
total_amount
int
payment_count
관계 요약
  1. #1 users ||--o{ cart_items · owns
  2. #2 users ||--o{ orders · places
  3. #3 users ||--o{ payments · makes
  4. #4 products ||--o{ cart_items · contains
  5. #5 products ||--o{ orders · contains
  6. #6 orders ||--|| payments · associated_with
원본 Mermaid 보기
erDiagram
    users ||--o{ cart_items : "owns"
    users ||--o{ orders : "places"
    users ||--o{ payments : "makes"
    products ||--o{ cart_items : "contains"
    products ||--o{ orders : "contains"
    orders ||--|| payments : "associated_with"

    users {
      bigint id PK
      string email UK
      string password
      string name
    }
    products {
      bigint id PK
      string name
      string category
      int price
      int stock_quantity
    }
    cart_items {
      bigint id PK
      bigint user_id FK
      bigint product_id FK
      int quantity
    }
    orders {
      bigint id PK
      bigint user_id FK
      bigint product_id FK
      int quantity
      int total_amount
      string status
      bigint payment_id
      datetime created_at
    }
    payments {
      bigint id PK
      bigint user_id FK
      int amount
      string payment_method
      string status
      datetime created_at
    }
    outbox_events {
      bigint id PK
      string event_type
      text payload
      string status
      datetime created_at
      datetime processed_at
    }
    daily_settlements {
      bigint id PK
      date report_date UK
      bigint total_amount
      int payment_count
    }
    monthly_settlements {
      bigint id PK
      string settlement_year_month UK
      bigint total_amount
      int payment_count
    }

시퀀스 다이어그램

시퀀스 다이어그램

sequence

시퀀스 다이어그램

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Participants
ClientGatewayOrderControllerOrderServiceProductServiceProductRepositoryPaymentServicePaymentRepositoryOrderRepository
  1. 1요청

    Client → Gateway

    POST /orders (productId, quantity, paymentMethod)

  2. 2요청

    Gateway → OrderController

    X-User-Id + Request

  3. 3요청

    OrderController → OrderService

    createOrder(userId, request)

  4. 4요청

    OrderService → ProductService

    GET /products/{id} (price lookup)

  5. 5저장

    ProductService → ProductRepository

    findById

  6. 6처리

    ProductRepository → ProductService

    Product

  7. 7처리

    ProductService → OrderService

    Product (price)

  8. 8요청

    OrderService → ProductService

    POST /internal/stocks/reserve

+ 13개 단계는 아래 상세 메시지에서 확인할 수 있습니다.

전체 메시지 상세
StepFrom → ToMessageCondition
1Client → GatewayPOST /orders (productId, quantity, paymentMethod)-
2Gateway → OrderControllerX-User-Id + Request-
3OrderController → OrderServicecreateOrder(userId, request)-
4OrderService → ProductServiceGET /products/{id} (price lookup)-
5ProductService → ProductRepositoryfindById-
6ProductRepository → ProductServiceProduct-
7ProductService → OrderServiceProduct (price)-
8OrderService → ProductServicePOST /internal/stocks/reserve-
9ProductService → ProductRepositorystock lookup, decrease-
10ProductRepository → ProductServicesuccess-
11ProductService → OrderService{ success: true }-
12OrderService → PaymentServicePOST /payments (userId, amount, method)-
13PaymentService → PaymentRepositorysave-
14PaymentRepository → PaymentServicePayment-
15PaymentService → OrderService{ paymentId, success: true }-
16OrderService → OrderRepositorysave(order)-
17OrderRepository → OrderServiceOrder-
18OrderService → OrderControllerOrderResponse-
19OrderController → Gateway201-
20Gateway → ClientOrderResponse-
21Note · OrderServiceOn failure: SAGA compensation / stock release / outbox recovery-
원본 Mermaid 보기
sequenceDiagram
    actor Client
    participant Gateway
    participant OrderController
    participant OrderService
    participant ProductService
    participant ProductRepository
    participant PaymentService
    participant PaymentRepository
    participant OrderRepository

    Client->>Gateway: POST /orders (productId, quantity, paymentMethod)
    Gateway->>OrderController: X-User-Id + Request
    OrderController->>OrderService: createOrder(userId, request)
    OrderService->>ProductService: GET /products/{id} (price lookup)
    ProductService->>ProductRepository: findById
    ProductRepository-->>ProductService: Product
    ProductService-->>OrderService: Product (price)
    OrderService->>ProductService: POST /internal/stocks/reserve
    ProductService->>ProductRepository: stock lookup, decrease
    ProductRepository-->>ProductService: success
    ProductService-->>OrderService: { success: true }
    OrderService->>PaymentService: POST /payments (userId, amount, method)
    PaymentService->>PaymentRepository: save
    PaymentRepository-->>PaymentService: Payment
    PaymentService-->>OrderService: { paymentId, success: true }
    OrderService->>OrderRepository: save(order)
    OrderRepository-->>OrderService: Order
    OrderService-->>OrderController: OrderResponse
    OrderController-->>Gateway: 201
    Gateway-->>Client: OrderResponse
    Note over OrderService,ProductService: On failure: SAGA compensation / stock release / outbox recovery