MSA ์ํคํ ์ฒ๋ ๋ฌด์์ธ๊ฐ
์๋น์ค๊ฐ ์ปค์ง์๋ก ์ฝ๋ ์์ ์ด ์ ์ ์ด๋ ค์์ง๋ค.
ํ ๋ถ๋ถ์ ๊ณ ์น๋ฉด ๋ค๋ฅธ ๋ถ๋ถ์ด ๋ง๊ฐ์ง๊ณ ,
๋ฐฐํฌ ํ ๋ฒ์ ์ ์ฒด ์๋น์ค๊ฐ ๋ฉ์ถ๋ค.
์ด๋ ๋ง์น ๊ฑฐ๋ํ ๋ ๊ณ ๋ธ๋ก ํ ๋ฉ์ด๋ฆฌ์ ๊ฐ๋ค.
ํ๋์ ๋ธ๋ก์ ๋ฐ๊พธ๋ ค๋ฉด ์ ์ฒด๋ฅผ ๋ถํดํด์ผ ํ๊ณ ,
์ค์๋ก ์๋ชป ๊ฑด๋๋ฆฌ๋ฉด ์ ์ฒด๊ฐ ๋ฌด๋์ง๋ค.
MSA(Microservices Architecture)๋
์ด ๊ฑฐ๋ํ ๋ฉ์ด๋ฆฌ๋ฅผ ์์ ์กฐ๊ฐ๋ค๋ก ๋๋์ด
๊ฐ๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ๊ฐ๋ฐ, ๋ฐฐํฌ, ์ด์ํ๋
ํ๋์ ์ธ ์ํํธ์จ์ด ์ํคํ
์ฒ ํจํด์ด๋ค.
์ MSA๋ก ์ ํํด์ผ ํ ๊น?
๋ฌธ์ 1: ๋ฐฐํฌ ๋ฆฌ์คํฌ
๋ชจ๋๋ฆฌ์ค๋ ํ ์ค ์์ ์๋ ์ ์ฒด๋ฅผ ์ฌ๋ฐฐํฌํด์ผ ํ๋ค.
๋ฌธ์ 2: ๊ธฐ์ ์ ํ์ ์ ์ฝ
์ ์ฒด๊ฐ ํ๋์ ์ธ์ด/ํ๋ ์์ํฌ์ ์ข
์๋๋ค.
๋ฌธ์ 3: ํ์ฅ์ฑ ํ๊ณ
ํน์ ๊ธฐ๋ฅ๋ง ํ์ฅํ๊ณ ์ถ์ด๋ ์ ์ฒด๋ฅผ ํ์ฅํด์ผ ํ๋ค.
๋ฌธ์ 4: ํ ํ์
์ด๋ ค์
์ฌ๋ฌ ํ์ด ํ๋์ ์ฝ๋๋ฒ ์ด์ค์์ ์ถฉ๋ํ๋ค.
๊ธฐ๋ณธ ๊ฐ๋ ์์ฝ
๐ท๏ธ ๋ชจ๋๋ฆฌ์ค vs MSA
๋ชจ๋๋ฆฌ์ค (Monolithic)
๊ฐ๋
: ๋ชจ๋ ๊ธฐ๋ฅ์ด ํ๋์ ์ ํ๋ฆฌ์ผ์ด์
์๋น ๋น์ :
ํ๋์ ๊ฑฐ๋ํ ์ฃผ๋ฐฉ์์
์ ์ฑ, ๋ฉ์ธ, ๋์ ํธ๋ฅผ ๋ชจ๋ ๋ง๋ ๋ค.
์
ฐํ ํ ๋ช
์ด ์ํ๋ฉด ์ ์ฒด ์ฃผ๋ฐฉ์ด ๋ฉ์ถ๋ค.
์ฅ์ :
- ๊ฐ๋จํ ๊ตฌ์กฐ
- ์ฌ์ด ๋๋ฒ๊น
- ํธ๋์ญ์ ๊ด๋ฆฌ ์ฉ์ด
๋จ์ :
- ๋ถ๋ถ ๋ฐฐํฌ ๋ถ๊ฐ
- ํ์ฅ์ฑ ์ ํ
- ๊ธฐ์ ์คํ ๊ณ ์
- ๋ณต์ก๋ ์ฆ๊ฐ
MSA (Microservices)
๊ฐ๋
: ๊ธฐ๋ฅ๋ณ๋ก ๋
๋ฆฝ๋ ์์ ์๋น์ค๋ค
์๋น ๋น์ :
์ ์ฑ ์ ๋ฌธ์ , ๋ฉ์ธ ์ ๋ฌธ์ , ๋์ ํธ ์ ๋ฌธ์ ์ผ๋ก ๋ถ๋ฆฌ.
๊ฐ ๋งค์ฅ์ ๋
๋ฆฝ์ ์ผ๋ก ์ด์๋๋ฉฐ,
ํ ๋งค์ฅ์ ๋ฌธ์ ๊ฐ ์๊ฒจ๋ ๋ค๋ฅธ ๋งค์ฅ์ ์ ์ ์ด์.
์ฅ์ :
- ๋ ๋ฆฝ์ ๋ฐฐํฌ
- ๊ธฐ์ ๋ค์์ฑ
- ์ ํ์ ํ์ฅ
- ํ ์์จ์ฑ
๋จ์ :
- ๋ณต์กํ ์ด์
- ๋คํธ์ํฌ ์ค๋ฒํค๋
- ๋ถ์ฐ ํธ๋์ญ์
- ๋๋ฒ๊น ์ด๋ ค์
๐ท๏ธ MSA ํต์ฌ ์์น
1. Single Responsibility (๋จ์ผ ์ฑ ์)
๊ฐ ์๋น์ค๋ ํ๋์ ๋น์ฆ๋์ค ๊ธฐ๋ฅ๋ง ๋ด๋น
์์:
- ์ฌ์ฉ์ ์๋น์ค: ํ์๊ฐ์ , ๋ก๊ทธ์ธ
- ์ฃผ๋ฌธ ์๋น์ค: ์ฃผ๋ฌธ ์์ฑ, ์กฐํ
- ๊ฒฐ์ ์๋น์ค: ๊ฒฐ์ ์ฒ๋ฆฌ
- ์๋ฆผ ์๋น์ค: ์ด๋ฉ์ผ, SMS ๋ฐ์ก
2. Decentralized Data (๋ถ์ฐ ๋ฐ์ดํฐ)
๊ฐ ์๋น์ค๋ ๋
๋ฆฝ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
[์ฌ์ฉ์ ์๋น์ค] โ [MySQL: users]
[์ฃผ๋ฌธ ์๋น์ค] โ [PostgreSQL: orders]
[๊ฒฐ์ ์๋น์ค] โ [MongoDB: payments]
3. API๋ฅผ ํตํ ํต์
์๋น์ค ๊ฐ ํต์ ์ ์ค์ง API๋ฅผ ํตํด์๋ง
ํต์ ๋ฐฉ์:
- REST API (๋๊ธฐ)
- gRPC (๋๊ธฐ, ๊ณ ์ฑ๋ฅ)
- Message Queue (๋น๋๊ธฐ)
4. ๋ ๋ฆฝ์ ๋ฐฐํฌ
๊ฐ ์๋น์ค๋ฅผ ๋ค๋ฅธ ์๋น์ค์ ๋
๋ฆฝ์ ์ผ๋ก ๋ฐฐํฌ
5. ์ฅ์ ๊ฒฉ๋ฆฌ (Fault Isolation)
ํ ์๋น์ค์ ์ฅ์ ๊ฐ ๋ค๋ฅธ ์๋น์ค์ ์ํฅ ์ต์ํ
๐ท๏ธ MSA ํต์ฌ ์ปดํฌ๋ํธ
1. API Gateway
์ญํ : ํด๋ผ์ด์ธํธ ์์ฒญ์ ์ ์ ํ ์๋น์ค๋ก ๋ผ์ฐํ
๊ธฐ๋ฅ:
- ์ธ์ฆ/์ธ๊ฐ
- ์์ฒญ ๋ผ์ฐํ
- ๋ก๋ ๋ฐธ๋ฐ์ฑ
- Rate Limiting
- ๋ก๊น /๋ชจ๋ํฐ๋ง
๋๊ตฌ: Kong, Spring Cloud Gateway, AWS API Gateway
2. Service Discovery
์ญํ : ์๋น์ค ์์น ์๋ ํ์
ํ์์ฑ:
MSA์์๋ ์๋น์ค๊ฐ ๋์ ์ผ๋ก ์์ฑ/์ญ์ ๋๋ฏ๋ก
IP์ ํฌํธ๋ฅผ ์๋์ผ๋ก ์ฐพ์์ผ ํ๋ค.
๋๊ตฌ: Eureka, Consul, Kubernetes Service
3. Configuration Management
์ญํ : ์ค์ํ๋ ์ค์ ๊ด๋ฆฌ
๋๊ตฌ: Spring Cloud Config, Consul, etcd
4. Message Broker
์ญํ : ๋น๋๊ธฐ ํต์ ๋ฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ
๋๊ตฌ: RabbitMQ, Kafka, AWS SQS
5. Container Orchestration
์ญํ : ์ปจํ
์ด๋ ๋ฐฐํฌ ๋ฐ ๊ด๋ฆฌ ์๋ํ
๋๊ตฌ: Kubernetes, Docker Swarm, ECS
MSA๋ก ์ ํํ๊ธฐ
์ ํ ์ ๋ต
1. Strangler Fig Pattern (๊ต์ด์ ๋ฌดํ๊ณผ ํจํด)
๊ฐ๋
: ์ ์ง์ ์ผ๋ก ๋ชจ๋๋ฆฌ์ค๋ฅผ MSA๋ก ๊ต์ฒด
๋จ๊ณ:
- ์ ๊ธฐ๋ฅ์ MSA๋ก ๊ฐ๋ฐ
- ๊ธฐ์กด ๊ธฐ๋ฅ์ ํ๋์ฉ MSA๋ก ์ด๋
- ๋ชจ๋๋ฆฌ์ค๋ฅผ ์ ์ง์ ์ผ๋ก ์ถ์
- ์ต์ข ์ ์ผ๋ก ๋ชจ๋๋ฆฌ์ค ์ ๊ฑฐ
[๋ชจ๋๋ฆฌ์ค 100%]
โ
[๋ชจ๋๋ฆฌ์ค 80% + MSA 20%]
โ
[๋ชจ๋๋ฆฌ์ค 50% + MSA 50%]
โ
[๋ชจ๋๋ฆฌ์ค 20% + MSA 80%]
โ
[MSA 100%]
2. Database-per-Service
์์น: ๊ฐ ์๋น์ค๊ฐ ๋
๋ฆฝ๋ DB ์์
์ ํ ๋ฐฉ๋ฒ:
- ์คํค๋ง ๋ถ๋ฆฌ
- ๋ฐ์ดํฐ ๋ณต์ (๋๊ธฐํ)
- API๋ฅผ ํตํ ๋ฐ์ดํฐ ์กฐํ
3. ์๋น์ค ๋ถ๋ฆฌ ๊ธฐ์ค
Domain-Driven Design (DDD) ๊ธฐ๋ฐ:
- Bounded Context ๋จ์๋ก ๋ถ๋ฆฌ
- Aggregate ๋จ์๋ก ๋ฐ์ดํฐ ๊ด๋ฆฌ
๋ถ๋ฆฌ ์์:
- ๊ฐ์ฅ ๋ ๋ฆฝ์ ์ธ ๊ธฐ๋ฅ๋ถํฐ
- ๋ณ๊ฒฝ์ด ์ฆ์ ๊ธฐ๋ฅ
- ํ์ฅ์ด ํ์ํ ๊ธฐ๋ฅ
- ๊ธฐ์ ์คํ ๋ณ๊ฒฝ์ด ํ์ํ ๊ธฐ๋ฅ
์ค์ ์์
Spring Boot MSA ๊ตฌ์ฑ
API Gateway (Spring Cloud Gateway)
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// ์ฌ์ฉ์ ์๋น์ค
.route("user-service", r -> r
.path("/api/users/**")
.filters(f -> f
.rewritePath("/api/users/(?<segment>.*)", "/users/${segment}")
.addRequestHeader("X-Gateway", "true"))
.uri("lb://USER-SERVICE"))
// ์ฃผ๋ฌธ ์๋น์ค
.route("order-service", r -> r
.path("/api/orders/**")
.uri("lb://ORDER-SERVICE"))
// ๊ฒฐ์ ์๋น์ค
.route("payment-service", r -> r
.path("/api/payments/**")
.uri("lb://PAYMENT-SERVICE"))
.build();
}
}
Service Discovery (Eureka)
Eureka Server:
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka Client (๊ฐ ์๋น์ค):
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
# application.yml
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
์๋น์ค ๊ฐ ํต์
1. REST API (๋๊ธฐ)
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public OrderResponse createOrder(OrderRequest request) {
// 1. ์ฌ์ฉ์ ์ ๋ณด ํ์ธ
String userUrl = "http://USER-SERVICE/users/" + request.getUserId();
User user = restTemplate.getForObject(userUrl, User.class);
if (user == null) {
throw new UserNotFoundException();
}
// 2. ์ฃผ๋ฌธ ์์ฑ
Order order = new Order();
order.setUserId(user.getId());
order.setItems(request.getItems());
orderRepository.save(order);
// 3. ๊ฒฐ์ ์์ฒญ
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.setOrderId(order.getId());
paymentRequest.setAmount(order.getTotalAmount());
String paymentUrl = "http://PAYMENT-SERVICE/payments";
PaymentResponse payment = restTemplate.postForObject(
paymentUrl,
paymentRequest,
PaymentResponse.class
);
return new OrderResponse(order, payment);
}
}
2. Message Queue (๋น๋๊ธฐ)
์ฃผ๋ฌธ ์๋น์ค โ ์ด๋ฒคํธ ๋ฐํ:
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(OrderRequest request) {
Order order = new Order();
order.setUserId(request.getUserId());
orderRepository.save(order);
// ์ฃผ๋ฌธ ์์ฑ ์ด๋ฒคํธ ๋ฐํ
OrderCreatedEvent event = new OrderCreatedEvent(
order.getId(),
order.getUserId(),
order.getTotalAmount()
);
rabbitTemplate.convertAndSend(
"order.exchange",
"order.created",
event
);
}
}
์๋ฆผ ์๋น์ค โ ์ด๋ฒคํธ ๊ตฌ๋ :
@Service
public class NotificationService {
@RabbitListener(queues = "notification.queue")
public void handleOrderCreated(OrderCreatedEvent event) {
// ์ฃผ๋ฌธ ์๋ฃ ์๋ฆผ ๋ฐ์ก
sendEmail(event.getUserId(), "์ฃผ๋ฌธ์ด ์๋ฃ๋์์ต๋๋ค.");
sendSMS(event.getUserId(), "์ฃผ๋ฌธ ๋ฒํธ: " + event.getOrderId());
}
}
Docker & Kubernetes ๋ฐฐํฌ
Dockerfile (๊ฐ ์๋น์ค)
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/user-service.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: myregistry/user-service:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: EUREKA_SERVER
value: "http://eureka-server:8761/eureka"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Circuit Breaker (์ฅ์ ๊ฒฉ๋ฆฌ)
Resilience4j ์ ์ฉ:
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
@Retry(name = "paymentService", fallbackMethod = "paymentFallback")
@TimeLimiter(name = "paymentService")
public CompletableFuture<PaymentResponse> processPayment(PaymentRequest request) {
return CompletableFuture.supplyAsync(() -> {
String url = "http://PAYMENT-SERVICE/payments";
return restTemplate.postForObject(url, request, PaymentResponse.class);
});
}
// Fallback ๋ฉ์๋
public CompletableFuture<PaymentResponse> paymentFallback(
PaymentRequest request,
Exception ex
) {
log.error("๊ฒฐ์ ์๋น์ค ํธ์ถ ์คํจ. Fallback ์คํ", ex);
// ๋์ฒด ๋ก์ง
PaymentResponse response = new PaymentResponse();
response.setStatus("PENDING");
response.setMessage("๊ฒฐ์ ์ฒ๋ฆฌ ์ค์
๋๋ค. ์ ์ ํ ํ์ธํด์ฃผ์ธ์.");
return CompletableFuture.completedFuture(response);
}
}
application.yml:
resilience4j:
circuitbreaker:
instances:
paymentService:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 5s
failureRateThreshold: 50
eventConsumerBufferSize: 10
retry:
instances:
paymentService:
maxAttempts: 3
waitDuration: 1s
timelimiter:
instances:
paymentService:
timeoutDuration: 3s
๋ถ์ฐ ํธ๋์ญ์ ์ฒ๋ฆฌ
๐ท๏ธ Saga Pattern
๊ฐ๋
: ๋ถ์ฐ ํ๊ฒฝ์์ ํธ๋์ญ์
์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋์ด ์ฒ๋ฆฌ
Choreography ๋ฐฉ์ (์ด๋ฒคํธ ๊ธฐ๋ฐ)
// 1. ์ฃผ๋ฌธ ์๋น์ค
@Service
public class OrderService {
public void createOrder(OrderRequest request) {
Order order = orderRepository.save(new Order(request));
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publish(new OrderCreatedEvent(order));
}
@EventListener
public void handlePaymentFailed(PaymentFailedEvent event) {
// ๋ณด์ ํธ๋์ญ์
: ์ฃผ๋ฌธ ์ทจ์
Order order = orderRepository.findById(event.getOrderId());
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
}
}
// 2. ๊ฒฐ์ ์๋น์ค
@Service
public class PaymentService {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
try {
Payment payment = processPayment(event);
eventPublisher.publish(new PaymentCompletedEvent(payment));
} catch (Exception ex) {
eventPublisher.publish(new PaymentFailedEvent(event.getOrderId()));
}
}
}
// 3. ๋ฐฐ์ก ์๋น์ค
@Service
public class DeliveryService {
@EventListener
public void handlePaymentCompleted(PaymentCompletedEvent event) {
Delivery delivery = createDelivery(event);
eventPublisher.publish(new DeliveryStartedEvent(delivery));
}
}
Orchestration ๋ฐฉ์ (์ค์ ์กฐ์จ)
@Service
public class OrderOrchestrator {
public void processOrder(OrderRequest request) {
String sagaId = UUID.randomUUID().toString();
try {
// 1. ์ฃผ๋ฌธ ์์ฑ
Order order = orderService.createOrder(request);
// 2. ๊ฒฐ์ ์ฒ๋ฆฌ
Payment payment = paymentService.processPayment(order);
// 3. ์ฌ๊ณ ์ฐจ๊ฐ
inventoryService.decreaseStock(order.getItems());
// 4. ๋ฐฐ์ก ์์
deliveryService.startDelivery(order);
// ๋ชจ๋ ์ฑ๊ณต
order.setStatus(OrderStatus.COMPLETED);
} catch (PaymentException ex) {
// ๋ณด์ ํธ๋์ญ์
orderService.cancelOrder(order.getId());
throw ex;
} catch (InventoryException ex) {
// ๋ณด์ ํธ๋์ญ์
paymentService.refund(payment.getId());
orderService.cancelOrder(order.getId());
throw ex;
}
}
}
MSA ๋ชจ๋ํฐ๋ง
๐ท๏ธ ๋ถ์ฐ ์ถ์ (Distributed Tracing)
Zipkin/Jaeger ์ฐ๋:
@Configuration
public class TracingConfig {
@Bean
public Tracer jaegerTracer() {
return Configuration.fromEnv("user-service")
.withSampler(new ConstSampler(true))
.withReporter(new LoggingReporter())
.getTracer();
}
}
์ฌ์ฉ ์์:
@Service
public class UserService {
@Autowired
private Tracer tracer;
public User getUser(Long id) {
Span span = tracer.buildSpan("getUser").start();
try {
span.setTag("userId", id);
User user = userRepository.findById(id);
span.log("User found: " + user.getName());
return user;
} catch (Exception ex) {
span.setTag("error", true);
span.log(ex.getMessage());
throw ex;
} finally {
span.finish();
}
}
}
๐ท๏ธ ์ค์ ๋ก๊น
ELK Stack (Elasticsearch, Logstash, Kibana):
# logstash.conf
input {
file {
path => "/var/log/services/*.log"
start_position => "beginning"
}
}
filter {
json {
source => "message"
}
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "services-%{+YYYY.MM.dd}"
}
}
MSA ์ํฐํจํด
โ ์ํฐํจํด 1: ๋๋ฌด ์์ ์๋น์ค
๋ฌธ์
โ ์๋ชป๋ ๋ถ๋ฆฌ:
- UserProfileService
- UserAddressService
- UserPhoneService
- UserEmailService
ํด๊ฒฐ
โ
์ ์ ํ ๋ถ๋ฆฌ:
- UserService (ํ๋กํ, ์ฃผ์, ์ฐ๋ฝ์ฒ ํตํฉ)
โ ์ํฐํจํด 2: ๊ณต์ ๋ฐ์ดํฐ๋ฒ ์ด์ค
๋ฌธ์
โ ์ฌ๋ฌ ์๋น์ค๊ฐ ํ๋์ DB ๊ณต์
[์ฃผ๋ฌธ ์๋น์ค] โโ
[๊ฒฐ์ ์๋น์ค] โโผโโ [๊ณต์ DB]
[๋ฐฐ์ก ์๋น์ค] โโ
ํด๊ฒฐ
โ
์๋น์ค๋ณ ๋
๋ฆฝ DB
[์ฃผ๋ฌธ ์๋น์ค] โ [์ฃผ๋ฌธ DB]
[๊ฒฐ์ ์๋น์ค] โ [๊ฒฐ์ DB]
[๋ฐฐ์ก ์๋น์ค] โ [๋ฐฐ์ก DB]
โ ์ํฐํจํด 3: ๋๊ธฐ ํธ์ถ ์ฒด์ธ
๋ฌธ์
// โ ๋๊ธฐ ํธ์ถ์ ์ฐ์
API Gateway โ Service A (100ms)
โ Service B (100ms)
โ Service C (100ms)
โ Service D (100ms)
์ด ์๋ต ์๊ฐ: 400ms + ๋คํธ์ํฌ ์ง์ฐ
ํด๊ฒฐ
// โ
๋น๋๊ธฐ ์ด๋ฒคํธ ๊ธฐ๋ฐ
API Gateway โ Service A (์ด๋ฒคํธ ๋ฐํ, ์ฆ์ ์๋ต)
โ ์ด๋ฒคํธ
Service B, C, D (๋ณ๋ ฌ ์ฒ๋ฆฌ)
์๋ต ์๊ฐ: 100ms
์ค์ ์ฒดํฌ๋ฆฌ์คํธ
โ MSA ๋์ ์
- ํ ๊ท๋ชจ๊ฐ ์ถฉ๋ถํ๊ฐ? (์ต์ 3๊ฐ ์ด์ ํ)
- DevOps ์ญ๋์ด ์๋๊ฐ?
- ๋ชจ๋๋ฆฌ์ค์ ๋ฌธ์ ๊ฐ ๋ช ํํ๊ฐ?
- ๋ณต์ก๋ ์ฆ๊ฐ๋ฅผ ๊ฐ๋นํ ์ ์๋๊ฐ?
โ ์๋น์ค ์ค๊ณ
- ๋จ์ผ ์ฑ ์ ์์น ์ค์
- ๋ ๋ฆฝ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค
- API ์ฐ์ ์ค๊ณ
- ๋ฒ์ ๊ด๋ฆฌ ์ ๋ต
- ์๋ฌ ์ฒ๋ฆฌ ํ์คํ
โ ์ด์ ์ธํ๋ผ
- API Gateway ๊ตฌ์ถ
- Service Discovery ๊ตฌํ
- ์ค์ ๋ก๊น ์์คํ
- ๋ถ์ฐ ์ถ์ ๋๊ตฌ
- ๋ชจ๋ํฐ๋ง ๋์๋ณด๋
โ ์์ ์ฑ
- Circuit Breaker ์ ์ฉ
- Retry ์ ์ฑ
- Timeout ์ค์
- Health Check
- Graceful Shutdown
โ ๋ณด์
- API Gateway ์ธ์ฆ
- ์๋น์ค ๊ฐ ์ธ์ฆ (mTLS)
- ๋น๋ฐ ์ ๋ณด ๊ด๋ฆฌ (Vault)
- ๋คํธ์ํฌ ๊ฒฉ๋ฆฌ
์์ฝ
MSA๋ ๋ณต์ก๋์ ์์จ์ฑ์ ํธ๋ ์ด๋์คํ๋ค.
๐ ํต์ฌ ํฌ์ธํธ:
- ๋จ์ผ ์ฑ ์: ์๋น์ค๋ ํ๋์ ๊ธฐ๋ฅ๋ง
- ๋ ๋ฆฝ ๋ฐฐํฌ: ๋ค๋ฅธ ์๋น์ค์ ๋ฌด๊ดํ๊ฒ ๋ฐฐํฌ
- ๋ฐ์ดํฐ ๋ถ๋ฆฌ: ๊ฐ ์๋น์ค๊ฐ ๋ ๋ฆฝ DB ์์
- API ํต์ : ์๋น์ค ๊ฐ ํต์ ์ API๋ก๋ง
- ์ฅ์ ๊ฒฉ๋ฆฌ: Circuit Breaker๋ก ์ฅ์ ์ ํ ์ฐจ๋จ
- ์ ์ง์ ์ ํ: Strangler Fig ํจํด ํ์ฉ
๐ ์ ํ ์์:
1๋จ๊ณ: ๋ชจ๋๋ฆฌ์ค ๋ถ์ ๋ฐ ์๋น์ค ๊ฒฝ๊ณ ์ ์
2๋จ๊ณ: API Gateway, Service Discovery ๊ตฌ์ถ
3๋จ๊ณ: ๋
๋ฆฝ์ฑ ๋์ ๊ธฐ๋ฅ๋ถํฐ ๋ถ๋ฆฌ
4๋จ๊ณ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ถ๋ฆฌ
5๋จ๊ณ: ๋ชจ๋ํฐ๋ง ๋ฐ ๋ก๊น
๊ตฌ์ถ
6๋จ๊ณ: ์ ์ง์ ์ผ๋ก ํ๋
โ ๏ธ ์ฃผ์์ฌํญ:
- ์์ ์๋น์ค๊ฐ ์ข์ ๊ฒ์ ์๋
- ๊ณต์ DB๋ MSA๊ฐ ์๋
- ๋๊ธฐ ํธ์ถ ์ฒด์ธ์ ํผํ ๊ฒ
- ๋ถ์ฐ ํธ๋์ญ์ ์ Saga ํจํด์ผ๋ก
- ๋ชจ๋ํฐ๋ง ์๋ MSA๋ ์ํ
๐ ๋์ ์๊ธฐ:
MSA๋ ๋ง๋ฅ์ด ์๋๋ค. ๋ค์ ์กฐ๊ฑด์ ํด๋นํ ๋ ๊ณ ๋ ค:
- ํ์ด 3๊ฐ ์ด์์ผ๋ก ๋ถ๋ฆฌ ๊ฐ๋ฅ
- ๋ฐฐํฌ ๋น๋๊ฐ ์ฃผ 1ํ ์ด์
- ํน์ ๊ธฐ๋ฅ์ ํ์ฅ ํ์
- ๊ธฐ์ ์คํ ๋ค์ํ ํ์
- DevOps ๋ฌธํ๊ฐ ์ ์ฐฉ๋จ
MSA๋ ๊ธฐ์ ์ ์ ํ์ด ์๋๋ผ
์กฐ์ง๊ณผ ๋น์ฆ๋์ค์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์๋จ์ด๋ค.
๋ฌด์กฐ๊ฑด ๋์
๋ณด๋ค๋ ํ์ฌ ๋ฌธ์ ๋ฅผ ์ ํํ ํ์
ํ๊ณ ,
MSA๊ฐ ์ง์ง ํด๊ฒฐ์ฑ
์ธ์ง ๋จผ์ ๊ณ ๋ฏผํด์ผ ํ๋ค.