OPEN HYPER STEP
← 목록으로 (Java+Spring)
JAVA · 94 / 99
java
CHAPTER 94 / 99
읽기 약 2
FUNCTION

이벤트 기반: Kafka 기초


핵심 개념

Producer/Consumer·Topic/Partition·Spring Kafka — 주문 이벤트 → 재고 감소.

본문

Kafka 핵심 개념

📋 코드 (11줄)
Topic: 메시지 분류 (예: orders, payments)
Partition: Topic 내 분할 (병렬 처리)
Producer: 메시지 발행
Consumer: 메시지 소비
Consumer Group: 같은 Topic을 함께 소비하는 그룹

특징:
- 영속성 (디스크 저장)
- 순서 보장 (Partition 내)
- 수평 확장 (Partition 추가)
- 재처리 가능 (offset reset)

Spring Kafka 설정

GRADLE📋 코드 (1줄)
implementation 'org.springframework.kafka:spring-kafka'
YAML📋 코드 (14줄)
spring:
  kafka:
    bootstrap-servers: localhost:9092
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all  # 모든 replica 확인
    consumer:
      group-id: order-service
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      auto-offset-reset: earliest
      properties:
        spring.json.trusted.packages: "com.example.event.*"

Producer

JAVA📋 코드 (31줄)
public record OrderPlacedEvent(
    Long orderId,
    Long userId,
    Long productId,
    int quantity,
    BigDecimal totalAmount,
    LocalDateTime placedAt
) {}


@Service
@RequiredArgsConstructor
public class OrderService {
    private final KafkaTemplate<String, Object> kafkaTemplate;
    private final OrderRepository orderRepo;

    @Transactional
    public Order placeOrder(OrderRequest req) {
        Order order = orderRepo.save(new Order(req));

        OrderPlacedEvent event = new OrderPlacedEvent(
            order.getId(), order.getUserId(),
            order.getProductId(), order.getQuantity(),
            order.getTotal(), LocalDateTime.now()
        );

        kafkaTemplate.send("orders", String.valueOf(order.getUserId()), event);
        // partition key — 같은 사용자의 주문은 같은 partition (순서 보장)
        return order;
    }
}

Consumer

JAVA📋 코드 (31줄)
@Component
@RequiredArgsConstructor
@Slf4j
public class InventoryEventListener {
    private final InventoryService inventoryService;

    @KafkaListener(topics = "orders", groupId = "inventory-service")
    public void handleOrderPlaced(OrderPlacedEvent event) {
        log.info("Order received: {}", event);

        try {
            inventoryService.decrementStock(event.productId(), event.quantity());
        } catch (Exception e) {
            log.error("재고 감소 실패: {}", event, e);
            // 재시도 또는 DLQ로
            throw e;
        }
    }
}


// 여러 Consumer Group — 같은 이벤트 다중 처리
@KafkaListener(topics = "orders", groupId = "notification-service")
public void notifyUser(OrderPlacedEvent event) {
    notificationService.sendOrderConfirmation(event);
}

@KafkaListener(topics = "orders", groupId = "analytics-service")
public void recordOrder(OrderPlacedEvent event) {
    analyticsService.track("order_placed", event);
}

재시도와 Dead Letter Queue

JAVA📋 코드 (29줄)
@Configuration
public class KafkaConfig {

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory(
        ConsumerFactory<String, Object> consumerFactory,
        KafkaTemplate<String, Object> kafkaTemplate
    ) {
        ConcurrentKafkaListenerContainerFactory<String, Object> factory =
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory);

        // 재시도 + DLQ
        DefaultErrorHandler errorHandler = new DefaultErrorHandler(
            new DeadLetterPublishingRecoverer(kafkaTemplate),
            new FixedBackOff(1000L, 3)  // 1초 간격, 3회 재시도
        );
        factory.setCommonErrorHandler(errorHandler);
        return factory;
    }
}


// DLQ Listener
@KafkaListener(topics = "orders.DLT", groupId = "dlt-handler")
public void handleDeadLetter(OrderPlacedEvent event) {
    log.error("Dead letter: {}", event);
    // Slack 알림 + 수동 처리
}

Idempotency — 중복 처리 방지

JAVA📋 코드 (19줄)
@Component
@RequiredArgsConstructor
public class OrderEventListener {
    private final RedisTemplate<String, String> redis;
    private final OrderProcessor processor;

    @KafkaListener(topics = "orders")
    public void handle(OrderPlacedEvent event) {
        String key = "processed:" + event.orderId();
        Boolean isNew = redis.opsForValue().setIfAbsent(key, "1", Duration.ofDays(7));

        if (Boolean.FALSE.equals(isNew)) {
            log.info("Already processed: {}", event.orderId());
            return;
        }

        processor.process(event);
    }
}

다음 챕터

CH.26 "분산 트랜잭션: SAGA" — 보상 트랜잭션.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 Spring 코드의 Kafka 이벤트 부분을 분석해서
재시도·DLQ·Idempotency와 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

Kafka 이벤트 vs 다른 패턴 비교를
실전 사례 5개로 보여주고 Kafka vs RabbitMQ vs Kinesis를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 코드베이스 전체를 분석해서
Kafka 이벤트 관련 메시지 손실 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 기업의 Kafka 이벤트 채택률과
한국 핀테크 Kafka 활용를 솔직히 알려줘.

⭐ 이것만 기억하세요
이벤트 기반: Kafka 기초 이 3가지만 확실히 잡으세요
1.Kafka는 영속·순서·재처리 가능 — 마이크로서비스 비동기 통신 표준
2.Producer/Consumer + Group으로 1:N 발행, Partition Key로 순서 보장
3.다음 챕터 CH.26에서 SAGA 패턴 — 분산 트랜잭션


공유하기
진행도 94 / 99