분산?
- Spring Event의 한계, 분산 환경으로 확장 필요성
- 메세지 브로커(Kafka 등)를 활용해서 여러 애플리케이션간 이벤트를 주고받는 개념
- 도메인 이벤트(Domain Event)와 통합 이벤트(Integration Event)의 차이
- Kafka의 동작 구조(Producer, Topic, Consumer, Broker) 이해하기
- Spring Event에서 Kafka 기반 이벤트로 확장하는 단계별 아키텍처 변화
애플리케이션 내부 이벤트의 한계
Spring Event는 단일 애플리케이션 내에서 비동기 처리를 구현하기에 매우 유용하기만,
분산 시스템 또는 마이크로서비스 환경에서는 한계가 존재한다고 함
1. Spring Event의 동작 범위
Spring Event는 Application Context 내부에서만 작동함
즉, 이벤트를 발행한 인스턴스(JVM) 안에서만 수신할 수 있음
2. 내부 이벤트만 사용했을때의 문제 예시
| 상황 | 문제점 |
| 회원가입 후 이메일 발송 | 요청을 처리한 인스턴스에서만 이벤트 발생 - > 이메일 누락 가능 |
| 주문 생성 후 결제 서비스 호출 | 주문 서비스에서만 이벤트가 전달됨 - > 결제 서비스와 데이터가 불일치하는 결과 |
| 서버 확장(Auto Scaling) | 인스턴스간 이벤트 전달 불가 - > 데이터의 일관성이 깨짐 |
메시지 브로커(Message Broker)
위의 문제들을 해결하기 위해 등장한게 메시지 브로커라고 함
Message Broker는 여러 애플리케이션간 메시지(이벤트)를 중간에서 안전하게 전달해주는 시스템이라고 한다.
- Apache Kafka(고성능, 대규모 로그 스트리밍)
- RabbitMQ(메시지 큐 기반 비동기 처리)
- Amazon SQS, Google Pub/Sub 등등..
1. 메시지 브로커의 역할
| 구성요소 | 설명 |
| Producer | 메시지를 발행하는 주체(Publisher 역할) |
| Broker | 메시지를 임시 저장, 관리하는 서버(Kafka Cluster 등등..) |
| Topic/Queue | 메시지를 구분하기 위한 논리적 공간(메일함과 유사하다고 함) |
| Consumer | 메시지를 구독하여 처리하는 주체 |
Kafka의 기본 구조
Kafka는 고성능 메시지 스트리밍 플랫폼이라고 한다.
이벤트를 중앙 Topic에 저장하고 다수의 소비자(Consumer)가 이를 병렬로 처리할 수 있도록 한다고 함
도메인 이벤트 vs 통합 이벤트
이벤트 기반 설계에서는 이벤트의 의미, 사용 범위를 명확하게 구분하는게 매우 중요하다고 함
1. 도메인 이벤트(Domain Event)
- 하나의 서비스 내부에서 발생하는 비즈니스 상태 변화를 표현함
- 이벤트를 통해서 내부 모듈간 결합도를 낮춤
public class UserRegisteredEvent { // 회원가입 완료시 이메일 발송을 위한 이벤트
private final String email;
public UserRegisteredEvent(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
}
도메인 이벤트는 Spring의 @EventListener 기반으로 비동기 처리할 수 있음
통합 이벤트(Integration Event)
- 서비스 간 데이터 교환을 위한 이벤트
- 다른 애플리케이션이 구독할 수 있도록 외부 브로커(Kafka)에 발행된다고 한다.
// 주문 서비스에서 결제 서비스로 전달되는 이벤트
public class OrderCreatedIntegrationEvent {
private final String orderId;
private final BigDecimal price;
public OrderCreatedIntegrationEvent(String orderId, BigDecimal price) {
this.orderId = orderId;
this.price = price;
}
public String getOrderId() {
return orderId;
}
public BigDecimal getPrice() {
return price;
}
}
| 구분 | 도메인 이벤트 | 통합 이벤트 |
| 범위 | 한 서비스 내부 | 서비스간 통신 |
| 처리 방식 | Spring Event | Kafka, RabbitMQ |
| 목적 | 내부 로직 분리 | 시스템간 데이터 전달 |
| 예시 | 회원가입 - > 이메일 발송 | 주문 생성 - > 결제 요청 |
Kafka와 Spring Event의 결합 구조
Spring Event로 내부 이벤트를 감지하고, Kafka로 외부 시스템에 통합 이벤트를 발행하는 구조라고 한다.
1. Producer 예제(이벤트 발행)
@Service
public class UserEventPublisher {
private final KafkaTemplate<String, String> kafkaTemplate;
public UserEventPublisher(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void publisherCreated(String userId) {
String message = String.format("{\"userId\":\"%s\"}", userId);
kafkaTemplate.send("user.events", message);
System.out.println("kafka 이벤트 발행 완료 : " + message);
}
}
2. Consumer 예제(이벤트 소비)
@Component
public class OrderServiceConsumer {
@KafkaListener(topics = "user.events", groupId = "order-service-group");
public void handleUserCreated(String message) {
System.out.println("Kafka 이벤트 수신 : " + message);
// 사용자 등록 이후 주문 초기화 로직을 짠다던지.. 등등..
}
}
이벤트 기반 아키텍처로의 발전 단계
Spring Event를 시작으로 해서, 점진적으로 Kafka를 도입하여 확장 가능한 이벤트 기반 아키텍처로 발전할 수 있다고 함
| 단계 | 설명 | 기술 스택 |
| 내부 이벤트 | 단일 인스턴스 내 비동기 이벤트 | ApplicationEventPublisher |
| 비동기 이벤트 | @Async 기반 빠른 응답 | @Async, ThreadPoolTaskExecutor |
| 통합 이벤트 | Kafka 등 메시지 브로커로의 확장 | KafkaTemplate, @KafkaListener |
| 완전한 이벤트 아키텍처 | CQRS, Event Sourcing 결합 | Kafka Streams, Debezium |
팁
| 상황 | 팁 |
| 여러 서비스에서 같은 Topic이 구독됨 | Consumer Group으로 병렬처리 및 중복방지 |
| 메시지 유실 방지 | Kafka Ack(확인 응답) 및 Retry 정책? 사용 |
| 이벤트 순서 보장 | Partition Key 지정(key = userId) |
| 트랜잭션 보장 | Kafka Transaction API로 원자성 확보하기 |
| 장애시 복구 | Dead Letter Queue(DLQ) 설계 필수 - 실무 |
정리
| 구분 | 설명 | 예시 |
| 내부 이벤트 | 단일 앱 내 이벤트 | @EventListener, ApplicationEventPublisher |
| 통합 이벤트 | 서비스간 메시지 전송 | Kafka, RabbitMQ |
| 도메인 이벤트 | 도메인 내부 상태 변화 | UserRegisteredEvent |
| 이벤트 기반 아키텍처 | 서비스간 결합도를 최소화한 시스템 구조 | Kafka Streams, CQRS |
'Spring Boot > 비동기 처리' 카테고리의 다른 글
| 비동기 처리 : 비동기 처리 방식 (1) | 2025.11.02 |
|---|---|
| 비동기 처리 : 비동기 처리를 왜 해야하는가 (0) | 2025.11.01 |
| Spring Event : 이벤트 기반 비동기 처리 활용사례 (0) | 2025.10.29 |
| Spring Event : 비동기 이벤트 처리 구현 (0) | 2025.10.25 |
| Spring Event : @EventListener 활용 (0) | 2025.10.25 |