Skip to content

Latest commit

ย 

History

History
519 lines (440 loc) ยท 14.9 KB

File metadata and controls

519 lines (440 loc) ยท 14.9 KB

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๋ฉด์ ‘

๋ฉด์ ‘๊ด€: "๋Œ€๊ทœ๋ชจ ์ด์ปค๋จธ์Šค ์‹œ์Šคํ…œ์„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ์„ค๊ณ„ํ•ด์ฃผ์„ธ์š”. ํŠนํžˆ ์„œ๋น„์Šค ๋ถ„๋ฆฌ์™€ ํ†ต์‹  ์ „๋žต์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”."

์ง€์›์ž: ๋„ค, ๋จผ์ € ๋ช‡ ๊ฐ€์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ ๊ทœ๋ชจ์™€ ํŠธ๋ž˜ํ”ฝ์€ ์–ด๋Š ์ •๋„์ธ๊ฐ€์š”?
  2. ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?
  3. ์„ฑ๋Šฅ์ด๋‚˜ ํ™•์žฅ์„ฑ ๊ด€๋ จ ํŠน๋ณ„ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๋‚˜์š”?
  4. ํ˜„์žฌ ๊ฒช๊ณ  ์žˆ๋Š” ์ฃผ์š” ๊ธฐ์ˆ ์  ๋ฌธ์ œ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

๋ฉด์ ‘๊ด€:

  1. ์ผ ํ‰๊ท  ์ฃผ๋ฌธ 10๋งŒ ๊ฑด, ํ”ผํฌ ์‹œ๊ฐ„๋Œ€๋Š” ํ‰๊ท ์˜ 5๋ฐฐ ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค.
  2. ๋น ๋ฅธ ๊ธฐ๋Šฅ ์ถœ์‹œ์™€ ์„œ๋น„์Šค ์•ˆ์ •์„ฑ์ด ๊ฐ€์žฅ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
  3. ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์€ 99.99% ๊ฐ€์šฉ์„ฑ์ด ํ•„์š”ํ•˜๋ฉฐ, ๊ฒฐ์ œ๋Š” 3์ดˆ ์ด๋‚ด ์‘๋‹ต์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  4. ํ˜„์žฌ ๋ชจ๋†€๋ฆฌ์‹ ์‹œ์Šคํ…œ์—์„œ ๋ฐฐํฌ๊ฐ€ ์–ด๋ ต๊ณ , ์ผ๋ถ€ ๊ธฐ๋Šฅ์˜ ์žฅ์• ๊ฐ€ ์ „์ฒด ์‹œ์Šคํ…œ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€์›์ž: ์ดํ•ดํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์„œ๋น„์Šค ๋ถ„๋ฆฌ ์ „๋žต

// 1. ๋„๋ฉ”์ธ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ๋ถ„๋ฆฌ
public class ServiceBoundaries {
    /*
    Core Services:
    - Product Service: ์ƒํ’ˆ ๊ด€๋ฆฌ
    - Order Service: ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ
    - Payment Service: ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
    - Inventory Service: ์žฌ๊ณ  ๊ด€๋ฆฌ
    - User Service: ํšŒ์› ๊ด€๋ฆฌ
    
    Supporting Services:
    - Notification Service: ์•Œ๋ฆผ ์ฒ˜๋ฆฌ
    - Analytics Service: ๋ฐ์ดํ„ฐ ๋ถ„์„
    - Search Service: ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
    
    Infrastructure Services:
    - API Gateway
    - Service Discovery
    - Configuration Service
    */
}

// 2. ์„œ๋น„์Šค ๋‹จ์œ„ ์˜ˆ์‹œ
@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentServiceClient paymentClient;
    private final InventoryServiceClient inventoryClient;
    
    @Transactional
    public OrderResult createOrder(OrderRequest request) {
        // 1. ์žฌ๊ณ  ํ™•์ธ
        InventoryResult inventory = inventoryClient.checkInventory(
            request.getProductId(), request.getQuantity());
            
        // 2. ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
        PaymentResult payment = paymentClient.processPayment(
            request.getPaymentInfo());
            
        // 3. ์ฃผ๋ฌธ ์ƒ์„ฑ
        Order order = orderRepository.save(
            Order.from(request, payment.getTransactionId()));
            
        return OrderResult.success(order);
    }
}

2. ์„œ๋น„์Šค ๊ฐ„ ํ†ต์‹  ์ „๋žต

// 1. ๋™๊ธฐ์‹ ํ†ต์‹  (REST)
@FeignClient(name = "payment-service")
public interface PaymentServiceClient {
    @PostMapping("/api/payments")
    PaymentResult processPayment(@RequestBody PaymentRequest request);
}

// 2. ๋น„๋™๊ธฐ์‹ ํ†ต์‹  (Event-Driven)
@Service
public class OrderEventHandler {
    private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
    
    public void publishOrderCreatedEvent(Order order) {
        OrderEvent event = OrderEvent.builder()
            .orderId(order.getId())
            .userId(order.getUserId())
            .status(OrderStatus.CREATED)
            .timestamp(Instant.now())
            .build();
            
        kafkaTemplate.send("order-events", event);
    }
    
    @KafkaListener(topics = "inventory-events")
    public void handleInventoryEvent(InventoryEvent event) {
        // ์žฌ๊ณ  ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
    }
}

๋ฉด์ ‘๊ด€: ์„œ๋น„์Šค ๊ฐ„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์€ ์–ด๋–ป๊ฒŒ ๋ณด์žฅํ•˜์‹œ๋‚˜์š”?

3. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ๋ณด์žฅ ์ „๋žต

3.1 Saga ํŒจํ„ด ๊ตฌํ˜„

@Service
public class OrderSagaCoordinator {
    
    // 1. ์ฃผ๋ฌธ ์ƒ์„ฑ Saga
    public OrderResult createOrder(OrderRequest request) {
        SagaInstance<OrderContext> saga = sagaManager.create(new OrderContext(request));
        
        return saga.executeSteps(List.of(
            // Step 1: ์žฌ๊ณ  ํ™•์ธ
            new SagaStep<>(
                ctx -> inventoryService.reserve(ctx.getProductId()),
                ctx -> inventoryService.cancelReservation(ctx.getProductId())
            ),
            // Step 2: ๊ฒฐ์ œ ์ฒ˜๋ฆฌ
            new SagaStep<>(
                ctx -> paymentService.process(ctx.getPaymentInfo()),
                ctx -> paymentService.refund(ctx.getTransactionId())
            ),
            // Step 3: ์ฃผ๋ฌธ ์ƒ์„ฑ
            new SagaStep<>(
                ctx -> orderRepository.create(ctx.getOrderDetails()),
                ctx -> orderRepository.markAsFailed(ctx.getOrderId())
            )
        ));
    }
    
    // 2. Saga ์ƒํƒœ ๊ด€๋ฆฌ
    @Getter
    public class SagaInstance<T> {
        private final String sagaId;
        private final T context;
        private final Map<Integer, StepStatus> stepStatuses = new HashMap<>();
        
        public void compensate(int fromStep) {
            // ๋ณด์ƒ ํŠธ๋žœ์žญ์…˜ ์‹คํ–‰
            for (int i = fromStep; i >= 0; i--) {
                steps.get(i).compensate(context);
                stepStatuses.put(i, StepStatus.COMPENSATED);
            }
        }
    }
}

3.2 ์ด๋ฒคํŠธ ์†Œ์‹ฑ

@Service
public class EventSourcingHandler {
    
    private final EventStore eventStore;
    
    // 1. ์ด๋ฒคํŠธ ์ €์žฅ
    public void saveEvent(OrderEvent event) {
        EventStream stream = eventStore.getStream(event.getAggregateId());
        stream.append(event);
        eventStore.save(stream);
        
        // ์ด๋ฒคํŠธ ๋ฐœํ–‰
        eventPublisher.publish(event);
    }
    
    // 2. ์ƒํƒœ ์žฌ๊ตฌ์„ฑ
    public Order reconstructOrderState(String orderId) {
        EventStream stream = eventStore.getStream(orderId);
        Order order = new Order();
        
        stream.getEvents().forEach(event -> {
            switch (event.getType()) {
                case ORDER_CREATED:
                    order.apply((OrderCreatedEvent) event);
                    break;
                case ORDER_UPDATED:
                    order.apply((OrderUpdatedEvent) event);
                    break;
                // ... ๊ธฐํƒ€ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
            }
        });
        
        return order;
    }
}

3.3 CQRS ํŒจํ„ด

// 1. Command ๋ชจ๋ธ
@Service
public class OrderCommandService {
    private final EventSourcingHandler eventHandler;
    
    @Transactional
    public void createOrder(CreateOrderCommand command) {
        OrderCreatedEvent event = OrderCreatedEvent.builder()
            .orderId(command.getOrderId())
            .userId(command.getUserId())
            .items(command.getItems())
            .build();
            
        eventHandler.saveEvent(event);
    }
}

// 2. Query ๋ชจ๋ธ
@Service
public class OrderQueryService {
    private final OrderReadRepository readRepository;
    
    public OrderDetailView getOrderDetails(String orderId) {
        return readRepository.findOrderDetails(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }
    
    // ์ด๋ฒคํŠธ ๊ตฌ๋…ํ•˜์—ฌ ์ฝ๊ธฐ ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ
    @EventListener
    public void on(OrderCreatedEvent event) {
        OrderDetailView view = OrderDetailView.from(event);
        readRepository.save(view);
    }
}

3.4 ์žฅ์•  ๋Œ€์‘ ๋ฐ ๋ณต๊ตฌ

@Service
public class ResiliencyManager {
    
    // 1. Circuit Breaker ๊ตฌํ˜„
    @CircuitBreaker(name = "payment")
    public PaymentResult processPayment(PaymentRequest request) {
        return paymentClient.processPayment(request);
    }
    
    // 2. ์žฌ์‹œ๋„ ์ •์ฑ…
    @Retryable(
        value = {ServiceTemporaryException.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000))
    public InventoryResult checkInventory(String productId) {
        return inventoryClient.checkAvailability(productId);
    }
    
    // 3. Fallback ์ฒ˜๋ฆฌ
    @Recover
    public PaymentResult paymentFallback(PaymentRequest request) {
        // ๋Œ€์ฒด ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ๋˜๋Š” ์‹คํŒจ ์ฒ˜๋ฆฌ
        return PaymentResult.failed("Service temporarily unavailable");
    }
}

3.5 ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์ถ”์ 

@Configuration
public class ObservabilityConfig {
    
    // 1. ๋ถ„์‚ฐ ์ถ”์ 
    @Bean
    public Tracer jaegerTracer() {
        return new Tracer.Builder("order-service")
            .withSampler(new ConstSampler(true))
            .withReporter(jaegerReporter())
            .build();
    }
    
    // 2. ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘
    @Bean
    public MeterRegistry meterRegistry() {
        return new SimpleMeterRegistry();
    }
    
    // 3. ๋กœ๊ทธ ์ง‘๊ณ„
    @Bean
    public LogstashAppender logstashAppender() {
        LogstashAppender appender = new LogstashAppender();
        appender.setDestination("logstash:5000");
        appender.setEncoding("UTF-8");
        return appender;
    }
}

์ด๋Ÿฌํ•œ ์ „๋žต๋“ค์„ ํ†ตํ•ด:

  1. ํŠธ๋žœ์žญ์…˜์˜ ์ผ๊ด€์„ฑ ๋ณด์žฅ
  2. ์„œ๋น„์Šค ๊ฐ„ ๋Š์Šจํ•œ ๊ฒฐํ•ฉ ์œ ์ง€
  3. ์žฅ์•  ๊ฒฉ๋ฆฌ์™€ ๋ณต๊ตฌ ๊ฐ€๋Šฅ
  4. ์‹œ์Šคํ…œ ๊ฐ€์‹œ์„ฑ ํ™•๋ณด

๋ฅผ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉด์ ‘๊ด€: ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์˜ ๋‹จ์ ๊ณผ ์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•œ ์ „๋žต์€ ๋ฌด์—‡์ธ๊ฐ€์š”?

4. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์˜ ๋‹จ์ ๊ณผ ๊ทน๋ณต ์ „๋žต

4.1 ๋ณต์žก์„ฑ ๊ด€๋ฆฌ

@Configuration
public class ServiceManagementConfig {

    // 1. API Gateway๋ฅผ ํ†ตํ•œ ๋ณต์žก์„ฑ ์ถ”์ƒํ™”
    @Bean
    public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("order-service", r -> r
                .path("/api/orders/**")
                .filters(f -> f
                    .circuitBreaker(c -> c.setFallbackUri("/fallback"))
                    .rateLimit(config -> config.setKeyResolver(userKeyResolver))
                    .retry(retryConfig))
                .uri("lb://order-service"))
            .build();
    }

    // 2. ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ
    @Bean
    public DiscoveryClient discoveryClient() {
        return ServiceDiscoveryBuilder.builder(ServiceInstance.class)
            .client(curatorFramework)
            .basePath("/services")
            .watchInstances(true)
            .build();
    }
}

4.2 ์šด์˜ ๋ณต์žก์„ฑ ํ•ด๊ฒฐ

@Configuration
public class OperationalToolingConfig {

    // 1. ์ค‘์•™ ์ง‘์ค‘์‹ ์„ค์ • ๊ด€๋ฆฌ
    @Bean
    public ConfigurationClient configClient() {
        return ConfigClientBuilder.create()
            .withConfigServer("config-server:8888")
            .withFailFast(true)
            .withRetry(true)
            .build();
    }

    // 2. ํ†ตํ•ฉ ๋ชจ๋‹ˆํ„ฐ๋ง
    @Bean
    public MetricsRegistry metricsRegistry() {
        return new CompositeMeterRegistry()
            .add(new PrometheusRegistry())
            .add(new GraphiteRegistry())
            .add(new ElasticRegistry());
    }

    // 3. ์ž๋™ํ™”๋œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ
    @Bean
    public DeploymentPipeline deploymentPipeline() {
        return DeploymentPipeline.builder()
            .withCanaryDeployment()
            .withBlueGreenDeployment()
            .withRollbackStrategy()
            .build();
    }
}

4.3 ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ๋ฌธ์ œ ํ•ด๊ฒฐ

@Service
public class DataConsistencyManager {

    // 1. ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”
    @EventListener
    public void handleDataChangeEvent(DataChangeEvent event) {
        switch (event.getType()) {
            case USER_UPDATED:
                syncUserData(event.getUserId());
                break;
            case ORDER_STATUS_CHANGED:
                syncOrderStatus(event.getOrderId());
                break;
        }
    }

    // 2. ๋ฐ์ดํ„ฐ ๋ฒ„์ €๋‹
    @Service
    public class DataVersionManager {
        private final VersionRepository versionRepository;

        public void updateData(String key, String value, long version) {
            if (!versionRepository.checkAndUpdate(key, version)) {
                throw new OptimisticLockException("Version conflict");
            }
            dataRepository.save(key, value, version + 1);
        }
    }
}

4.4 ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ ํ•ด๊ฒฐ

@Service
public class NetworkResiliencyManager {

    // 1. ํƒ€์ž„์•„์›ƒ ๊ด€๋ฆฌ
    @Bean
    public RestTemplate resilientRestTemplate() {
        return RestTemplateBuilder.create()
            .setConnectTimeout(Duration.ofSeconds(3))
            .setReadTimeout(Duration.ofSeconds(5))
            .build();
    }

    // 2. ๋ฒŒํฌํ—ค๋“œ ํŒจํ„ด
    @Bean
    public ThreadPoolTaskExecutor serviceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(50);
        executor.setRejectedExecutionHandler(new CallerRunsPolicy());
        return executor;
    }

    // 3. ์„œํ‚ท๋ธŒ๋ ˆ์ด์ปค
    @CircuitBreaker(name = "serviceCall", fallbackMethod = "fallback")
    public ServiceResponse callService(ServiceRequest request) {
        return serviceClient.call(request);
    }
}

4.5 ํ…Œ์ŠคํŠธ ๋ณต์žก์„ฑ ํ•ด๊ฒฐ

@SpringBootTest
public class ServiceIntegrationTests {

    // 1. ๊ณ„์•ฝ ํ…Œ์ŠคํŠธ
    @Test
    public void verifyServiceContract() {
        // Consumer Driven Contract Testing
        ContractVerifier.verify(consumerContract)
            .againstProvider(providerStub);
    }

    // 2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ
    @TestConfiguration
    public class TestConfig {
        @Bean
        public TestContainers testContainers() {
            return new TestContainers()
                .withKafka()
                .withMongo()
                .withRedis();
        }
    }

    // 3. ์นด์˜ค์Šค ์—”์ง€๋‹ˆ์–ด๋ง
    @Test
    public void performChaosTest() {
        chaosMonkey.injectLatency()
            .withLatency(Duration.ofMillis(500))
            .withProbability(0.1);
            
        verify(serviceMetrics.getLatency())
            .isLessThan(Duration.ofSeconds(1));
    }
}

4.6 ๋น„์šฉ ๊ด€๋ฆฌ

@Configuration
public class CostOptimizationConfig {

    // 1. ์ž์› ์‚ฌ์šฉ๋Ÿ‰ ๋ชจ๋‹ˆํ„ฐ๋ง
    @Bean
    public ResourceMonitor resourceMonitor() {
        return ResourceMonitor.builder()
            .withCPUMonitoring()
            .withMemoryMonitoring()
            .withNetworkMonitoring()
            .build();
    }

    // 2. ์ž๋™ ์Šค์ผ€์ผ๋ง
    @Bean
    public AutoScaler autoScaler() {
        return AutoScaler.builder()
            .withMetricBasedScaling()
            .withScheduleBasedScaling()
            .withCostOptimization()
            .build();
    }

    // 3. ๋น„์šฉ ๋ถ„์„
    @Scheduled(cron = "0 0 0 * * *")
    public void analyzeCosts() {
        costAnalyzer.generateReport()
            .byService()
            .byResource()
            .byTimeRange()
            .export();
    }
}

์ด๋Ÿฌํ•œ ์ „๋žต๋“ค์„ ํ†ตํ•ด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์˜ ์ฃผ์š” ๋‹จ์ ๋“ค์„ ๊ทน๋ณตํ•˜๋ฉด์„œ๋„ ์žฅ์ ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ต์‹ฌ์€:

  1. ์ ์ ˆํ•œ ๋„๊ตฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ
  2. ์ž๋™ํ™”๋œ ์šด์˜ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  3. ๊ฒฌ๊ณ ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น… ์‹œ์Šคํ…œ
  4. ์ฒด๊ณ„์ ์ธ ํ…Œ์ŠคํŠธ ์ „๋žต
  5. ํšจ์œจ์ ์ธ ๋น„์šฉ ๊ด€๋ฆฌ

๋ฅผ ํ†ตํ•ด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์˜ ๋ณต์žก์„ฑ์„ ๊ด€๋ฆฌ ๊ฐ€๋Šฅํ•œ ์ˆ˜์ค€์œผ๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.