백엔드 데이터 인프라 130편 — Spring Kafka 종합 + 시리즈 완주 (마지막 글)

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 130편. Spring Kafka 종합 + 시리즈 130편 완주 — KafkaTemplate·@KafkaListener·@EnableKafka·@EnableKafkaStreams·KafkaAdmin·Spring Boot 자동 구성·운영 패턴을 종합한 학습 노트. 시리즈 1 자바 백엔드 입문 59편과 자연스럽게 이어지는 closing.

📚 백엔드 데이터 인프라 · 130편 — Spring Kafka 종합 + 시리즈 완주 (마지막 글)

이 글은 백엔드 데이터 인프라 시리즈 마지막 130편이에요. 129편 까지 Kafka Streams 의 모든 영역을 끝냈다면, 이번 130편은 Spring 환경 통합을 종합하고 시리즈 완주를 안내합니다.

Spring Kafka 가 어렵게 느껴지는 이유

Spring 추상화는 vanilla Java Kafka client (순정 자바 클라이언트) 위에 한 겹 더 얹은 layer (계층) 입니다. 각 영역마다 별도 abstraction (추상화) 을 둡니다.

  • KafkaTemplate = Producer 추상화
  • @KafkaListener = Consumer 추상화
  • KafkaAdmin = Admin 추상화
  • @EnableKafkaStreams = Streams 추상화
  • @RetryableTopic = Retry·DLQ 패턴

대부분은 자동이고, Spring Boot starter 만 쓰면 boilerplate (반복 코드) 가 거의 0 입니다.

이 글에서 5가지 영역을 종합하고, Spring Boot 통합 패턴을 정리하고, 시리즈를 완주합니다.

Setup — spring-kafka 의존성

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

<!-- Streams 사용 시 -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-streams</artifactId>
</dependency>

Spring Boot 가 auto-configuration (자동 구성) 을 해주니, application.yml 만으로 시작할 수 있어요.

1. Producer — KafkaTemplate

기본 설정

spring:
  kafka:
    bootstrap-servers: kafka1:9092,kafka2:9092
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all
      properties:
        enable.idempotence: true
        compression.type: zstd
        linger.ms: 10

사용

@Service
public class OrderEventPublisher {

    @Autowired
    private KafkaTemplate<String, Order> kafkaTemplate;

    public void publish(Order order) {
        CompletableFuture<SendResult<String, Order>> future =
            kafkaTemplate.send("orders", order.getId(), order);

        future.whenComplete((result, ex) -> {
            if (ex != null) {
                log.error("Failed to send", ex);
            } else {
                RecordMetadata metadata = result.getRecordMetadata();
                log.info("Sent to {} p={} offset={}",
                    metadata.topic(), metadata.partition(), metadata.offset());
            }
        });
    }
}

KafkaTemplate.send()CompletableFuture 를 반환합니다. Spring 의 비동기 패턴이에요.

Transactional Producer

spring:
  kafka:
    producer:
      transaction-id-prefix: tx-
@Service
@Transactional("kafkaTransactionManager")
public class TransactionalService {

    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;

    public void process() {
        kafkaTemplate.send("topic-a", "message-1");
        kafkaTemplate.send("topic-b", "message-2");
        // 둘 다 atomic
    }
}

@Transactional 만 붙이면 transaction wrapping 이 자동입니다. 자세한 EOS (Exactly-Once Semantics, 정확히 한 번 처리 보장) 는 88·112편을 참고하세요.

2. Consumer — @KafkaListener

기본 설정

spring:
  kafka:
    consumer:
      group-id: order-workers
      auto-offset-reset: earliest
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      properties:
        spring.json.trusted.packages: "*"
    listener:
      ack-mode: MANUAL
      concurrency: 3

concurrency: 3 은 한 application 안에 consumer instance 3개 (thread 3개) 를 띄운다는 뜻입니다.

사용

@Component
public class OrderProcessor {

    @KafkaListener(topics = "orders", groupId = "order-workers")
    public void handle(Order order, Acknowledgment ack) {
        try {
            processOrder(order);
            ack.acknowledge();   // manual commit
        } catch (Exception e) {
            log.error("Failed", e);
            // Retry 또는 DLQ
        }
    }
}

Batch Listener

@KafkaListener(topics = "events")
public void handleBatch(List<Event> events, Acknowledgment ack) {
    events.forEach(this::process);
    ack.acknowledge();
}
spring:
  kafka:
    listener:
      type: batch

대량 처리 환경에 효율적입니다.

동적 Topic·Partition

@KafkaListener(
    topicPartitions = @TopicPartition(
        topic = "orders",
        partitions = {"0", "1"}
    )
)
public void handle(ConsumerRecord<String, Order> record) {
    // 특정 partition 만 처리
}

특수 사례에 쓰고, 보통은 topics 만 씁니다.

3. Retry + DLQ — @RetryableTopic

Spring Kafka 2.7+ 의 강력한 기능이에요. 재시도와 DLQ (Dead Letter Queue, 실패 메시지 격리 큐) 가 자동입니다.

@RetryableTopic(
    attempts = "3",
    backoff = @Backoff(delay = 1000, multiplier = 2.0),
    dltTopicSuffix = "-dlt",
    autoCreateTopics = "true"
)
@KafkaListener(topics = "orders")
public void handle(Order order) {
    if (someTemporaryFailure()) {
        throw new RuntimeException("temporary");   // 자동 retry
    }
    process(order);
}

@DltHandler
public void handleDlt(Order order, @Header(KafkaHeaders.EXCEPTION_MESSAGE) String error) {
    log.error("DLT received: {} - {}", order, error);
    // 영구 실패 처리
}

다음 topic 들이 자동으로 만들어집니다.

  • orders-retry-0 (1초 후 재시도)
  • orders-retry-1 (2초 후)
  • orders-retry-2 (4초 후)
  • orders-dlt (모두 실패 시)

복잡한 retry 로직이 어노테이션 한 줄로 끝납니다.

4. Admin — KafkaAdmin + Topic Provisioning

@Configuration
public class KafkaTopicConfig {

    @Bean
    public KafkaAdmin kafkaAdmin(KafkaProperties properties) {
        return new KafkaAdmin(properties.buildAdminProperties());
    }

    @Bean
    public NewTopic ordersTopic() {
        return TopicBuilder.name("orders")
            .partitions(3)
            .replicas(2)
            .config(TopicConfig.RETENTION_MS_CONFIG, "604800000")
            .build();
    }

    @Bean
    public NewTopic paymentsTopic() {
        return TopicBuilder.name("payments")
            .partitions(5)
            .replicas(3)
            .build();
    }
}

Application 이 시작할 때 topic 이 자동으로 만들어집니다. 이미 있으면 건너뛰고요. CI/CD 친화적이에요.

5. Streams — @EnableKafkaStreams

spring:
  kafka:
    streams:
      application-id: my-streams-app
      properties:
        processing.guarantee: exactly_once_v2
        num.standby.replicas: 1
        num.stream.threads: 4
@Configuration
@EnableKafkaStreams
public class StreamsConfig {

    @Bean
    public KStream<String, Order> orderStream(StreamsBuilder builder) {
        KStream<String, Order> orders = builder.stream("orders");

        orders
            .filter((k, order) -> order.getAmount() > 100)
            .mapValues(order -> enrich(order))
            .to("enriched-orders");

        return orders;
    }
}

@EnableKafkaStreams 가 lifecycle (시작·종료 수명주기) 을 알아서 관리합니다.

6. Spring Boot 통합 — 운영 환경

spring:
  kafka:
    bootstrap-servers: ${KAFKA_BROKERS}
    properties:
      security.protocol: SASL_SSL
      sasl.mechanism: SCRAM-SHA-512
      sasl.jaas.config: >
        org.apache.kafka.common.security.scram.ScramLoginModule required
        username="${KAFKA_USER}"
        password="${KAFKA_PASSWORD}";
      ssl.truststore.location: ${KAFKA_TRUSTSTORE}
      ssl.truststore.password: ${KAFKA_TRUSTSTORE_PASSWORD}

    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all
      properties:
        enable.idempotence: true
        compression.type: zstd

    consumer:
      group-id: ${spring.application.name}
      auto-offset-reset: earliest
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer

    listener:
      ack-mode: MANUAL
      concurrency: 3

    streams:
      application-id: ${spring.application.name}-streams
      properties:
        processing.guarantee: exactly_once_v2
        num.standby.replicas: 1

환경변수와 secrets 만 갈아끼우면 dev·staging·prod 가 같은 코드로 돕니다.

7. Health Check — Spring Actuator

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus,kafka-streams
  health:
    kafka:
      enabled: true
    kafka-streams:
      enabled: true

/actuator/health 응답에 Kafka·Streams 상태가 같이 실립니다.

Kubernetes liveness·readiness probe:

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080

8. 운영 권장 최종 체크리스트

  • [ ] Bootstrap = 3+ broker 명시
  • [ ] Security = SASL_SSL + SCRAM-SHA-512 + ssl.truststore
  • [ ] Producer = acks=all + idempotence + zstd
  • [ ] Consumer = manual ack + concurrency + group-id unique
  • [ ] Streams = EOS V2 + standby replicas + RF 3
  • [ ] Topic provisioning = @Bean NewTopic + RF 3
  • [ ] Retry·DLQ = @RetryableTopic 또는 ErrorHandler
  • [ ] Health check = Actuator + K8s probe
  • [ ] Monitoring = JMX Exporter + Prometheus + Grafana
  • [ ] Secrets = 환경변수 또는 K8s Secret + ConfigProvider
  • [ ] Logging = Spring Kafka package INFO

시리즈 130편 완주 — Roadmap 회고

백엔드 데이터 인프라 130편 완주!

📚 Part 1~3: PostgreSQL (1~46) ✓
   - 튜토리얼·SQL Language·운영

📚 Part 4: Redis (47~79) ✓
   - 자료구조 6종·키 관리·상호작용·패턴·운영·모듈·클라이언트

📚 Part 5: Kafka (80~130) ✓
   - 기본·Design·API·Config·Operations·고급 인프라·Internals·Security·Connect·Streams·Spring 통합

이 시리즈의 동반자가 자바 백엔드 입문 59편 입니다.

시리즈 1과 시리즈 2를 합치면 총 189편이에요.

Java 백엔드 + PG + Redis + Kafka — 현대 백엔드의 95% 를 한 번에 훑은 셈입니다.

어디부터 다시 봐야 할까

도메인별로 깊이가 필요한 영역을 모았어요.

  • 데이터 모델링 = 36편 PG indexes·44편 PG queries·52편 Redis sets·53편 Redis sorted sets·64편 Twitter clone
  • 트랜잭션 = 38편 PG MVCC·59편 Redis transactions·60편 Lua·88·112편 Kafka EOS
  • 분산 시스템 = 67·68편 Redis replication/cluster·89·105편 Kafka replication/KRaft
  • 이벤트 driven = 54편 Redis Streams·80~130편 Kafka 전체
  • 성능 최적화 = 41편 PG performance·72편 Redis memory·85편 Kafka efficiency·109편 Network Layer
  • 보안 = 70·71편 Redis ACL/TLS·113~116편 Kafka security
  • 운영 = 99·100편 Kafka operations·monitoring·105편 KRaft·129편 Streams 운영

다음 단계 — Hands-on

이론에서 실전으로 넘어가는 길은 이렇습니다.

  1. Docker Compose 로 PG + Redis + Kafka 로컬 cluster
  2. Spring Boot 예제 = 작은 microservice 작성
  3. Confluent Cloud 무료 plan = 진짜 Kafka 환경
  4. Toy Project = e-commerce backend (PG 영속·Redis 캐시·Kafka 이벤트)

끝맺음

한 줄 — 완주 축하합니다! 백엔드 데이터 인프라의 모든 큰 영역 을 한 번에 본 셈이에요. 이제 진짜 작업 으로.

이 시리즈가 도움 됐다면 자바 백엔드 입문 59편 도 같이 보면 좋아요. 그 위에 이 시리즈가 자연스럽게 얹힙니다.

질문이나 피드백은 댓글이나 contact 으로 남겨주세요. 같이 학습합니다.

시험 직전 한 번 더 — Spring Kafka 함정 압축 노트

  • 의존성 = spring-kafka + (Streams 시) kafka-streams
  • Producer = KafkaTemplate.send()CompletableFuture
  • Transactional Producer = transaction-id-prefix + @Transactional("kafkaTransactionManager")
  • Consumer = @KafkaListener + Acknowledgment (manual ack)
  • concurrency: 3 = 한 app 에 3 consumer thread
  • Batch Listener = listener.type=batch + List<T> 인자
  • @RetryableTopic = 자동 retry topic 들 + DLT
  • @DltHandler = 영구 실패 처리
  • Admin = KafkaAdmin + @Bean NewTopic (TopicBuilder)
  • 시작 시 자동 topic 생성
  • Streams = @EnableKafkaStreams + @Bean KStream
  • 자동 lifecycle 관리
  • 운영 환경 config — 환경변수 + Secrets + 모든 환경 같은 코드
  • Health Check = Spring Actuator + K8s liveness/readiness probe
  • 운영 체크리스트 — Security·Producer·Consumer·Streams·Topic·Retry·Health·Monitoring·Secrets·Logging
  • 시리즈 완주 130편 — PG 46 + Redis 33 + Kafka 51
  • 동반자 = 자바 백엔드 입문 59편 → 총 189편
  • 다음 단계 — Docker Compose 로컬·Confluent Cloud·Toy Project

공식 문서: Spring Kafka Documentation 에서 자세한 기능을 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글: 시리즈 마지막 편이에요.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!