Sam Baek, The Dev's Corner

๐Ÿ—๏ธ MSA ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„์™€ ์ „ํ™˜ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

05 Nov 2025

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๋กœ ๊ต์ฒด

๋‹จ๊ณ„:

  1. ์ƒˆ ๊ธฐ๋Šฅ์€ MSA๋กœ ๊ฐœ๋ฐœ
  2. ๊ธฐ์กด ๊ธฐ๋Šฅ์„ ํ•˜๋‚˜์”ฉ MSA๋กœ ์ด๋™
  3. ๋ชจ๋†€๋ฆฌ์Šค๋ฅผ ์ ์ง„์ ์œผ๋กœ ์ถ•์†Œ
  4. ์ตœ์ข…์ ์œผ๋กœ ๋ชจ๋†€๋ฆฌ์Šค ์ œ๊ฑฐ


[๋ชจ๋†€๋ฆฌ์Šค 100%]
โ†“
[๋ชจ๋†€๋ฆฌ์Šค 80% + MSA 20%]
โ†“
[๋ชจ๋†€๋ฆฌ์Šค 50% + MSA 50%]
โ†“
[๋ชจ๋†€๋ฆฌ์Šค 20% + MSA 80%]
โ†“
[MSA 100%]


2. Database-per-Service


์›์น™: ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋…๋ฆฝ๋œ DB ์†Œ์œ 

์ „ํ™˜ ๋ฐฉ๋ฒ•:

  1. ์Šคํ‚ค๋งˆ ๋ถ„๋ฆฌ
  2. ๋ฐ์ดํ„ฐ ๋ณต์ œ (๋™๊ธฐํ™”)
  3. API๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์กฐํšŒ


3. ์„œ๋น„์Šค ๋ถ„๋ฆฌ ๊ธฐ์ค€


Domain-Driven Design (DDD) ๊ธฐ๋ฐ˜:

  • Bounded Context ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌ
  • Aggregate ๋‹จ์œ„๋กœ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ


๋ถ„๋ฆฌ ์ˆœ์„œ:

  1. ๊ฐ€์žฅ ๋…๋ฆฝ์ ์ธ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ
  2. ๋ณ€๊ฒฝ์ด ์žฆ์€ ๊ธฐ๋Šฅ
  3. ํ™•์žฅ์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ
  4. ๊ธฐ์ˆ  ์Šคํƒ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๊ธฐ๋Šฅ


์‹ค์ „ ์˜ˆ์‹œ


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๋Š” ๋ณต์žก๋„์™€ ์ž์œจ์„ฑ์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋‹ค.

๐Ÿ’Ž ํ•ต์‹ฌ ํฌ์ธํŠธ:

  1. ๋‹จ์ผ ์ฑ…์ž„: ์„œ๋น„์Šค๋Š” ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ๋งŒ
  2. ๋…๋ฆฝ ๋ฐฐํฌ: ๋‹ค๋ฅธ ์„œ๋น„์Šค์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ฐฐํฌ
  3. ๋ฐ์ดํ„ฐ ๋ถ„๋ฆฌ: ๊ฐ ์„œ๋น„์Šค๊ฐ€ ๋…๋ฆฝ DB ์†Œ์œ 
  4. API ํ†ต์‹ : ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹ ์€ API๋กœ๋งŒ
  5. ์žฅ์•  ๊ฒฉ๋ฆฌ: Circuit Breaker๋กœ ์žฅ์•  ์ „ํŒŒ ์ฐจ๋‹จ
  6. ์ ์ง„์  ์ „ํ™˜: Strangler Fig ํŒจํ„ด ํ™œ์šฉ


๐Ÿš€ ์ „ํ™˜ ์ˆœ์„œ:

1๋‹จ๊ณ„: ๋ชจ๋†€๋ฆฌ์Šค ๋ถ„์„ ๋ฐ ์„œ๋น„์Šค ๊ฒฝ๊ณ„ ์ •์˜
2๋‹จ๊ณ„: API Gateway, Service Discovery ๊ตฌ์ถ•
3๋‹จ๊ณ„: ๋…๋ฆฝ์„ฑ ๋†’์€ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๋ถ„๋ฆฌ
4๋‹จ๊ณ„: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ถ„๋ฆฌ
5๋‹จ๊ณ„: ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น… ๊ตฌ์ถ•
6๋‹จ๊ณ„: ์ ์ง„์ ์œผ๋กœ ํ™•๋Œ€

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ:

  • ์ž‘์€ ์„œ๋น„์Šค๊ฐ€ ์ข‹์€ ๊ฒƒ์€ ์•„๋‹˜
  • ๊ณต์œ  DB๋Š” MSA๊ฐ€ ์•„๋‹˜
  • ๋™๊ธฐ ํ˜ธ์ถœ ์ฒด์ธ์€ ํ”ผํ•  ๊ฒƒ
  • ๋ถ„์‚ฐ ํŠธ๋žœ์žญ์…˜์€ Saga ํŒจํ„ด์œผ๋กœ
  • ๋ชจ๋‹ˆํ„ฐ๋ง ์—†๋Š” MSA๋Š” ์œ„ํ—˜


๐Ÿ“Š ๋„์ž… ์‹œ๊ธฐ:

MSA๋Š” ๋งŒ๋Šฅ์ด ์•„๋‹ˆ๋‹ค. ๋‹ค์Œ ์กฐ๊ฑด์— ํ•ด๋‹นํ•  ๋•Œ ๊ณ ๋ ค:

  • ํŒ€์ด 3๊ฐœ ์ด์ƒ์œผ๋กœ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅ
  • ๋ฐฐํฌ ๋นˆ๋„๊ฐ€ ์ฃผ 1ํšŒ ์ด์ƒ
  • ํŠน์ • ๊ธฐ๋Šฅ์˜ ํ™•์žฅ ํ•„์š”
  • ๊ธฐ์ˆ  ์Šคํƒ ๋‹ค์–‘ํ™” ํ•„์š”
  • DevOps ๋ฌธํ™”๊ฐ€ ์ •์ฐฉ๋จ


MSA๋Š” ๊ธฐ์ˆ ์  ์„ ํƒ์ด ์•„๋‹ˆ๋ผ
์กฐ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์ˆ˜๋‹จ์ด๋‹ค.
๋ฌด์กฐ๊ฑด ๋„์ž…๋ณด๋‹ค๋Š” ํ˜„์žฌ ๋ฌธ์ œ๋ฅผ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ ,
MSA๊ฐ€ ์ง„์งœ ํ•ด๊ฒฐ์ฑ…์ธ์ง€ ๋จผ์ € ๊ณ ๋ฏผํ•ด์•ผ ํ•œ๋‹ค.