MSA
Categories:tech
MSA
발생
최근에 회사에서 새로운 프로젝트를 시작하면서 아키텍처에 대해 고민하고 있다. 기존에는 모놀리식 아키텍처를 사용했는데, 팀이 커지고 서비스가 복잡해지면서 마이크로서비스 아키텍처로 전환을 고려하고 있다.
마이크로서비스가 정말 모든 문제를 해결해주는 만능 해결책일까? 장단점을 제대로 파악해보자.
모놀리식 vs 마이크로서비스
모놀리식 아키텍처
모놀리식 아키텍처는 모든 기능이 하나의 애플리케이션으로 구성된 구조다.
┌─────────────────────────────────┐
│ 모놀리식 애플리케이션 │
├─────────────────────────────────┤
│ 사용자 관리 │ 주문 관리 │ 결제 관리 │
│ 상품 관리 │ 재고 관리 │ 배송 관리 │
└─────────────────────────────────┘
모놀리식의 특징
- 단일 배포: 전체 애플리케이션을 한 번에 배포
- 공유 데이터베이스: 하나의 데이터베이스 사용
- 단일 기술 스택: 하나의 언어/프레임워크 사용
마이크로서비스 아키텍처
마이크로서비스 아키텍처는 독립적인 서비스들로 구성된 구조다.
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 사용자 │ │ 주문 │ │ 결제 │
│ 서비스 │ │ 서비스 │ │ 서비스 │
└─────────┘ └─────────┘ └─────────┘
│ │ │
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 사용자 │ │ 주문 │ │ 결제 │
│ DB │ │ DB │ │ DB │
└─────────┘ └─────────┘ └─────────┘
마이크로서비스의 특징
- 독립적 배포: 각 서비스를 독립적으로 배포
- 분산 데이터베이스: 서비스별로 데이터베이스 분리
- 다양한 기술 스택: 서비스별로 다른 기술 사용 가능
마이크로서비스의 장점
1. 독립적 개발과 배포
# 서비스별 독립적 배포
services:
user-service:
image: user-service:v1.2.0
order-service:
image: order-service:v2.1.0
payment-service:
image: payment-service:v1.0.5
2. 기술적 다양성
사용자 서비스: Node.js + MongoDB
주문 서비스: Java + PostgreSQL
결제 서비스: Python + Redis
3. 확장성
# 서비스별 독립적 확장
user-service:
replicas: 3
order-service:
replicas: 5
payment-service:
replicas: 2
4. 장애 격리
한 서비스의 장애가 다른 서비스에 직접적인 영향을 주지 않는다.
마이크로서비스의 단점
1. 복잡성 증가
네트워크 통신
// 서비스 간 통신
const userService = 'http://user-service:3000';
const orderService = 'http://order-service:3001';
// 사용자 정보 조회
const user = await fetch(`${userService}/users/${userId}`);
// 주문 생성
const order = await fetch(`${orderService}/orders`, {
method: 'POST',
body: JSON.stringify({ userId: user.id, items })
});
분산 트랜잭션
// 분산 트랜잭션 처리의 복잡성
try {
// 1. 주문 생성
const order = await orderService.createOrder(orderData);
// 2. 재고 차감
await inventoryService.decreaseStock(order.items);
// 3. 결제 처리
await paymentService.processPayment(order.id, paymentData);
} catch (error) {
// 롤백 처리의 복잡성
await rollbackTransaction(order.id);
}
2. 데이터 일관성
분산 데이터베이스 문제
사용자 서비스 DB: 사용자 정보
주문 서비스 DB: 주문 정보
결제 서비스 DB: 결제 정보
→ 데이터 일관성 보장의 어려움
3. 운영 복잡성
모니터링
# 각 서비스별 모니터링 필요
services:
- user-service
- order-service
- payment-service
- inventory-service
- notification-service
로그 관리
// 분산 로그 추적
const traceId = generateTraceId();
logger.info('Order created', { traceId, orderId, userId });
// 다른 서비스에서도 같은 traceId 사용
logger.info('Payment processed', { traceId, orderId, paymentId });
마이크로서비스 구현 패턴
1. API Gateway 패턴
// API Gateway
app.get('/api/users/:id', async (req, res) => {
const user = await userService.getUser(req.params.id);
res.json(user);
});
app.post('/api/orders', async (req, res) => {
const order = await orderService.createOrder(req.body);
res.json(order);
});
2. 서비스 디스커버리
# Consul 설정 예시
services:
user-service:
address: user-service
port: 3000
health: /health
order-service:
address: order-service
port: 3001
health: /health
3. 이벤트 기반 아키텍처
// 이벤트 발행
eventBus.publish('order.created', {
orderId: order.id,
userId: order.userId,
items: order.items
});
// 이벤트 구독
eventBus.subscribe('order.created', async (event) => {
await inventoryService.reserveItems(event.items);
await notificationService.sendOrderConfirmation(event.userId);
});
4. CQRS (Command Query Responsibility Segregation)
// Command (쓰기)
class CreateOrderCommand {
constructor(userId, items) {
this.userId = userId;
this.items = items;
}
}
// Query (읽기)
class GetOrderQuery {
constructor(orderId) {
this.orderId = orderId;
}
}
마이크로서비스 도입 시 고려사항
1. 도메인 경계 파악
도메인별 서비스 분리:
- 사용자 관리 도메인
- 주문 처리 도메인
- 결제 처리 도메인
- 재고 관리 도메인
2. 데이터 분리 전략
-- 서비스별 데이터베이스 분리
-- 사용자 서비스 DB
CREATE TABLE users (
id UUID PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
-- 주문 서비스 DB
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID, -- 외부 참조
status VARCHAR(20)
);
3. 통신 방식 선택
동기 통신 (HTTP/gRPC)
// HTTP 통신
const response = await fetch('http://user-service/users/123');
const user = await response.json();
비동기 통신 (Message Queue)
// 메시지 큐를 통한 비동기 통신
await messageQueue.publish('user.updated', {
userId: '123',
name: 'John Doe'
});
마이크로서비스가 적합한 경우
적합한 경우
- 대규모 팀: 여러 팀이 독립적으로 개발
- 복잡한 도메인: 비즈니스 로직이 복잡하고 독립적
- 다양한 기술: 서비스별로 다른 기술 스택 필요
- 높은 확장성: 서비스별 독립적 확장 필요
부적합한 경우
- 소규모 팀: 개발 리소스가 제한적
- 단순한 도메인: 비즈니스 로직이 단순
- 빠른 개발: 빠른 프로토타이핑이 필요한 경우
- 데이터 일관성: 강한 일관성이 중요한 경우
결론
마이크로서비스는 만능 해결책이 아니다. 프로젝트의 규모, 팀의 상황, 비즈니스 요구사항을 종합적으로 고려해야 한다.
도입 전 고려사항:
- 팀 규모와 역량: 마이크로서비스를 운영할 수 있는 역량
- 비즈니스 복잡성: 도메인 경계가 명확한지
- 운영 복잡성: 모니터링, 배포, 장애 대응 능력
- 점진적 전환: 모놀리식에서 점진적으로 전환
마이크로서비스는 복잡성을 증가시키지만, 적절한 상황에서 도입하면 큰 이점을 얻을 수 있다. 중요한 것은 무작정 도입하는 것이 아니라, 프로젝트에 맞는 최적의 아키텍처를 선택하는 것이다.
Day-22