백엔드 데이터 인프라 130편. Spring Kafka 종합 + 시리즈 130편 완주 — KafkaTemplate·@KafkaListener·@EnableKafka·@EnableKafkaStreams·KafkaAdmin·Spring Boot 자동 구성·운영 패턴을 종합한 학습 노트. 시리즈 1 자바 백엔드 입문 59편과 자연스럽게 이어지는 closing.
이 글은 백엔드 데이터 인프라 시리즈 마지막 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
이론에서 실전으로 넘어가는 길은 이렇습니다.
- Docker Compose 로 PG + Redis + Kafka 로컬 cluster
- Spring Boot 예제 = 작은 microservice 작성
- Confluent Cloud 무료 plan = 진짜 Kafka 환경
- 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 에서 자세한 기능을 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 125편 — Kafka Streams DSL (변환·집계·Join·Window)
- 126편 — Kafka Streams Processor API (Low-Level)
- 127편 — Kafka Streams Stateful + Interactive Queries
- 128편 — Kafka Streams Testing (TopologyTestDriver)
- 129편 — Kafka Streams 운영 (Security · Manage · Reset)
다음 글: 시리즈 마지막 편이에요.