이벤트 기반 아키텍처는 분산 시스템에서 컴포넌트가 서로 직접 통신하지 않고, 이벤트를 통해 간접적으로 상호작용하는 설계 방안입니다. 이를 통해 각 구성 요소가 독립적으로 운영되며 확장성과 유연성이 높아집니다. 주요 구성 요소와 함께 이벤트 기반 아키텍처가 분산화에 적합한 이유는 다음과 같습니다.
이벤트 기반 아키텍처의 핵심 개념
- 이벤트(Event): 시스템 내의 상태 변화를 의미합니다. 예를 들어, “주문 완료”, “결제 완료” 등의 사건이 발생하면 해당 이벤트가 발행됩니다.
- 이벤트 브로커(Event Broker): 이벤트를 발행하고 구독할 수 있는 중앙 허브 역할을 하며, Kafka, RabbitMQ, AWS SNS/SQS 등이 자주 사용됩니다.
- 발행자(Publisher)와 구독자(Subscriber): 이벤트를 발생시키는 발행자와 이벤트를 수신하고 이에 응답하는 구독자가 독립적으로 존재하며, 발행자와 구독자는 서로를 알 필요 없이 이벤트 브로커를 통해 통신합니다.
이벤트 기반 아키텍처의 특징
- 독립적인 서비스 운영: 서비스들이 직접 통신하지 않고 이벤트로 연결되므로 각각의 서비스는 독립적으로 배포, 확장, 운영이 가능합니다.
- 확장성: 구독자가 늘어나거나 줄어드는 일이 서비스 운영에 영향을 미치지 않기 때문에 분산화된 서비스 구성에서 확장성이 용이합니다.
- 비동기 통신: 이벤트가 발생하면 브로커에 전달되고 구독자들은 비동기 방식으로 이벤트를 처리할 수 있어 높은 성능을 유지할 수 있습니다.
분산화 측면에서의 장점
- 높은 유연성: 새로운 서비스나 기능이 추가될 때 기존 서비스에 대한 수정 없이 브로커에 새 구독자를 추가함으로써 확장이 가능합니다.
- 결합도 감소: 서비스 간의 결합도를 낮추어 각 서비스가 독립적으로 유지보수와 운영이 가능하므로 시스템의 전체적인 유연성이 향상됩니다.
- 복원력: 서비스 장애가 발생하더라도 이벤트가 브로커에 쌓이므로 복구 시 이벤트를 순차적으로 처리하여 데이터를 잃지 않게 보장할 수 있습니다.
설계 시 고려사항
- 이벤트 순서 보장: 이벤트가 순서대로 처리되어야 할 경우 특별한 설계가 필요합니다. 예를 들어, Kafka는 파티셔닝을 통해 순서 보장을 지원합니다.
- 데이터 일관성: 비동기 방식으로 데이터 일관성을 유지하는 데 주의해야 하며, 필요 시 보상 트랜잭션 또는 이벤트 소싱을 통해 최종 일관성을 보장할 수 있습니다.
- 모니터링과 로깅: 분산화된 이벤트 흐름을 추적하기 위해 중앙 모니터링과 로깅 시스템을 활용하는 것이 중요합니다. 로그 집계를 통해 장애 상황을 신속하게 파악할 수 있도록 합니다.
이벤트 기반 아키텍처의 예시
이벤트 기반 아키텍처를 실제로 구현하는 과정에서는 여러 요소와 설계 고려사항을 단계별로 검토해야 합니다. 여기서는 전자상거래 시스템을 예로 들어, 결제 및 배송 프로세스를 중심으로 하는 이벤트 기반 아키텍처 솔루션 설계를 설명하겠습니다.
요구사항 분석 및 아키텍처 설계
요구사항
- 결제 완료 시 배송 시스템에서 자동으로 배송 준비가 되어야 합니다.
- 주문 상태 변경을 트래킹해야 하며, 고객에게 알림을 전송해야 합니다.
- 결제, 배송, 알림 서비스는 독립적으로 배포 및 확장 가능해야 합니다.
아키텍처 설계
- 이벤트 브로커 선택: Apache Kafka를 이벤트 브로커로 사용하여 메시지를 관리할 수 있습니다. Kafka는 고성능, 파티셔닝을 통한 확장성, 그리고 순서 보장을 제공합니다.
- 서비스 정의:
- Order 서비스: 주문을 생성하고 관리하며, 결제 완료 이벤트를 발행.
- Payment 서비스: 결제 완료 시 이벤트를 발행.
- Shipping 서비스: 결제 완료 이벤트를 구독해 상품을 준비 및 배송.
- Notification 서비스: 모든 이벤트(주문 생성, 결제 완료, 배송 완료 등)를 구독하여 고객에게 상태를 알림.
이벤트 흐름 및 데이터 설계
이벤트 정의
주문 생성: { "eventType": "ORDER_CREATED", "orderId": "123", "userId": "abc", "amount": 100.0 }
결제 완료: { "eventType": "PAYMENT_COMPLETED", "orderId": "123", "paymentId": "789" }
배송 준비 완료: { "eventType": "SHIPPING_READY", "orderId": "123", "shippingId": "456" }
이벤트 메시지는 JSON 형식으로 정의하며, 각 이벤트에 대한 타입, ID, 주요 정보들을 포함하여 구독자가 해당 이벤트를 식별할 수 있도록 합니다.
서비스 구현
이벤트 발행자와 구독자 설계
각 서비스는 이벤트 브로커(Kafka)를 통해 간접적으로 통신합니다.
- Order 서비스
- 주문 생성 시
ORDER_CREATED
이벤트 발행. - 결제 서비스가 결제 완료 시,
PAYMENT_COMPLETED
이벤트 수신.
- 주문 생성 시
- Payment 서비스
- 결제 완료 시
PAYMENT_COMPLETED
이벤트 발행. - 결제가 완료되면 Order 서비스에 별도 요청 없이 결제 완료 이벤트를 발행합니다.
- 결제 완료 시
- Shipping 서비스
PAYMENT_COMPLETED
이벤트를 구독하여 배송 준비 수행 후SHIPPING_READY
이벤트를 발행.- 배송이 완료되면 고객에게도 알림 전송.
- Notification 서비스
- 모든 이벤트를 구독하여 적절한 알림을 사용자에게 전송합니다.
이벤트 처리 흐름
- Order 서비스가 주문을 생성하고
ORDER_CREATED
이벤트를 발행합니다. - Payment 서비스는 결제 완료 후
PAYMENT_COMPLETED
이벤트를 발행합니다. - Shipping 서비스는
PAYMENT_COMPLETED
이벤트를 구독하여, 해당 주문의 배송 준비를 수행하고SHIPPING_READY
이벤트를 발행합니다. - Notification 서비스는 각 이벤트를 구독하여 주문 생성, 결제 완료, 배송 준비 완료 등의 상태 변경 알림을 고객에게 전달합니다.
코드 예시 (간단한 Kafka Publisher & Subscriber)
다음은 Kafka로 PAYMENT_COMPLETED
이벤트를 발행하고 구독하는 Java 코드 예시입니다.
// Publisher: Payment 서비스에서 이벤트 발행
Producer<String, String> producer = new KafkaProducer<>(properties);
String message = "{ \"eventType\": \"PAYMENT_COMPLETED\", \"orderId\": \"123\", \"paymentId\": \"789\" }";
producer.send(new ProducerRecord<>("payment-events", "123", message));
producer.close();
// Subscriber: Shipping 서비스에서 이벤트 구독
Consumer<String, String> consumer = new KafkaConsumer<>(properties);
consumer.subscribe(Arrays.asList("payment-events"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
// Process payment completed event
System.out.printf("Received payment event: %s%n", record.value());
}
}
시스템 확장과 장애 처리 설계
확장성
- 서비스 간 직접 호출이 아닌 이벤트 발행/구독 방식이므로, 각 서비스는 독립적으로 확장할 수 있습니다. 예를 들어, 트래픽이 높은 Notification 서비스는 구독자 수를 늘려 확장할 수 있습니다.
장애 처리
- 이벤트가 일시적으로 지연되거나 일부 서비스가 다운되는 경우, Kafka는 메시지를 재전송하거나 메시지 보존 기간 내에 누락 없이 다시 제공할 수 있어 복구가 용이합니다.
- 이벤트 저장을 통해 복원력이 증가하고, 장애 발생 시 이벤트 재처리를 통해 데이터 일관성도 유지할 수 있습니다.
모니터링 및 로깅
모니터링을 위해 Prometheus와 Grafana를 활용하여 각 서비스 상태와 이벤트 지연 시간을 시각화합니다. 로그는 ELK(Elasticsearch, Logstash, Kibana) 스택을 통해 중앙 관리하여, 이벤트 처리 흐름을 추적하고 장애 발생 원인을 파악하는 데 유용합니다.
이벤트 기반 아키텍처는 시스템의 결합도를 낮추고, 독립적인 서비스 개발과 배포를 가능하게 하여, 대규모 시스템에서 높은 유연성과 확장성을 제공합니다.