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 대시보드)
- Zipkin 연동
- Docker 구성
- Dockerfile : 각 서비스별 작성
- docker-compose.yml
- PostgreSQL
- Redis
- Zipkin
- 각 Spring Service
7. 시퀀스 다이어그램
시퀀스 다이어그램은 핵심 로직만 잡고 넘어가려고 한다. 다이어그램 작성은 PlantUML을 사용하였다.
- 주문 생성
- 주문 조회
- 상품 추가
앞으로의 진행은 서비스 간 통신 로직을 세팅하기위해 FeignClient를 설정하고 DB, 비즈니스 로직, 외부 API 연결까지 한다음에 조회 API에 캐시를 붙일 예정이다.
그리고 아직 할 수 있을지 없을진 모르겠지만 추후 리팩토링을 통해 Kafka로 주문 이벤트 발행하는 것까지 확장시켜볼까 생각만 하는 중...