Spring/MSA

[MSA] MSA 기반 상품 주문 시스템 (1)

챛채 2025. 3. 7. 12:18

MSA의 개념을 확립하고자 작은 프로젝트를 하나 해보려고 한다.

1. 프로젝트 개요

  • 프로젝트명 : MSA 기반 상품 주문 시스템
  • 목표 : MSA, JWT 인증, Redis 캐싱, 분산 추적, Docker CI/CD 실습

2. 아키텍처 설계 흐름

Client -> Gateway(인증, 인가, 라우팅) -> Eureka로 서비스 발견(Load Balancing) -> 각 서비스 호출(Auth, Product, Order)

 

3. ERD 설계

  • Auth DB(auth-db)
CREATE TABLE users (
    user_id BIGSERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);
  • Product DB(product-db)
CREATE TABLE products (
    product_id BIGSERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    supply_price INT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);
  • Order DB(order-db)
CREATE TABLE orders (
    order_id BIGSERIAL PRIMARY KEY,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_products (
    id BIGSERIAL PRIMARY KEY,
    order_id BIGINT NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
    product_id BIGINT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

 

4. API 명세서

  • Auth Service
    • 로그인 후 JWT를 헤더에 담아서 전달
Method URL 설명 요청 예시 응답 예시
POST /auth/sign-up 회원 가입 { "username": "test", "password": "1234" } 201 Created
POST /auth/sign-in 로그인 (JWT 발급) { "username": "test", "password": "1234" } { "token": "JWT_TOKEN" }
  • Product Service
    • 상품 추가 시 캐시 무효화 처리
    • 상품 목록 조회 시 캐싱 1분 유지
Method URL 설명 요청 예시 응답 예시
POST /products 상품 추가 { "name": "사과", "supplyPrice": 1000 } 201 Created
GET /products 상품 목록 조회   [ { "productId": 1, "name": "사과", "supplyPrice": 1000 } ]
  • Order Service
    • 주문 생성 시 FeginClient로 상품 존재 여부 검증
    • 주문 조회 결과는 1분동안 Redis 캐싱
Method URL 설명 요청 예시 응답 예시
POST /orders 주문 생성 { "product_ids": [1, 2] } { "order_id": 1 }
POST /orders?fail 주문 실패 { "productIds": [999] } { "message": "잠시 후 다시 시도해주세요." }
PUT /orders/{orderId} 주문에 상품 추가 { "productId": 3 } { "orderId": 1, "productIds": [1, 2, 3] }
GET /orders/{orderId} 주문 조회   { "orderId": 1, "productIds": [1, 2, 3] }

5. 서비스별 의존성 정리

- Eureka Server(19090)

  • Spring Web
  • Eureka Server
  • Actuator

- Gateway(19091)

  • Spring Web
  • Spring Cloud Gateway
  • Eureka Discovery Client
  • Spring Boot Actuator
  • Spring Security (JWT 필터 만들기용)

- Auth Service(19095)

  • Spring Web
  • Spring Data JPA
  • PostgreSQL Driver
  • Eureka Discovery Client
  • Spring Security
  • JWT (라이브러리는 implementation으로 직접 추가)
  • Lombok
  • Validation
  • Spring Boot Actuator

- Product Service(19093/19094)

  • Spring Web
  • Spring Data JPA
  • PostgreSQL Driver
  • Eureka Discovery Client
  • Spring Security
  • Redis (Spring Data Redis)
  • Lombok
  • Validation
  • Spring Boot Actuator

- Order Service(19092)

  • Spring Web
  • Spring Data JPA
  • PostgreSQL Driver
  • Eureka Discovery Client
  • Spring Security
  • OpenFeign (상품 검증용)
  • Redis (Spring Data Redis)
  • Lombok
  • Validation
  • Spring Boot Actuator
  • Resilience4j (Fallback 처리용)

6. 상세 설계 

  • 인증/ 인가
    • 회원 가입/로그인 -> JWT 발급
    • Gateway에서 JWT 검증 필터 처리
    • 인증되지 않은 요청 -> 401 Unauthorized
  • 로드 밸런싱
    • 상품 서비스 2개 인스턴스 실행(19093, 19094)
    • Weighted Load Balancer로 70:30 요청 분산
    • 응답 헤더에 server-port 출력
  • 캐시 설계 -Redis
    • 상품 목록 조회  
      • 상품 추가 후에는 캐시 무효화 (POST /products를 했는데 캐시에는 옛날 데이터가 남아있어서 목록에 안 보일 가능성 있기 때문)
    • 주문 조회 
      • 동일 주문 반복 조회 최적화를 위해 60초 유지 후 자동 만료
      • 너무 오래 캐시하면 상태가 바뀌었는데도 옛날 정보 줄 수 있으니 짧게 유지
  • 장애 대응
    • 상품 서비스 호출 실패 시 Fallback 처리
    • "잠시후 주문 추가를 요청해주세요." 메세지 반환
  • 분산추적
    • Zipkin 연동
      • 서비스간 호출 추적
      • 호출 시간 분석(Zipkin 대시보드)
  • Docker 구성
    • Dockerfile : 각 서비스별 작성
    • docker-compose.yml
      • PostgreSQL
      • Redis
      • Zipkin
      • 각 Spring Service

7. 시퀀스 다이어그램

시퀀스 다이어그램은 핵심 로직만 잡고 넘어가려고 한다. 다이어그램 작성은 PlantUML을 사용하였다.

  • 주문 생성

 

  • 주문 조회

  • 상품 추가

 

 

앞으로의 진행은 서비스 간 통신 로직을 세팅하기위해 FeignClient를 설정하고 DB, 비즈니스 로직, 외부 API 연결까지 한다음에 조회 API에 캐시를 붙일 예정이다. 

 

그리고 아직 할 수 있을지 없을진 모르겠지만 추후 리팩토링을 통해 Kafka로 주문 이벤트 발행하는 것까지 확장시켜볼까 생각만 하는 중...