이벤트는 무엇이고 언제 사용해야할까
MSA, 혹은 멀티 모듈 같이, 하나의 애플리케이션 서버를 도메인 혹은 사내 규정에 따른 기능을 기준으로 분류하는 서비스들은 거의 예외 없이 ‘이벤트’ 프로그래밍을 선호합니다.
그러다 보니, 요즘에는 거의 rabbitMQ 혹은 kafka를 사용할 줄 알아야 하는 경우가 많습니다. 그러나, 이 두 기술 중 하나 혹은 혼용해야 한다는 것은 결국 별도의 ‘브로커 서버’가 추가되는 것이고 서버가 추가된다는 것은 네트워크 통신 횟수가 늘어난다는 뜻입니다. 따라서, 만약에 ‘이벤트’ 프로그래밍을 왜 해야 하고, 선택에 따른 트레이드오프를 계산할 줄 모른다면, 오히려 서비스의 응답시간을 늘리는 요소가 될 수 있습니다.
이벤트?
이벤트의 사전적 의미는 ‘사건’입니다. 그런데, 프로그래밍 용어는 정말 평범한 용어를 참 다양한 상황에 따라 다르게 해석하고 활용하는 경우가 많습니다. 그러니, 좀 더 이벤트를 세밀하게 정의할 필요가 있겠습니다.
이벤트는 아래와 같은 뜻을 가지고 있습니다.
- 일어난 일의 기록입니다.
- 변경 또는 삭제할 수 없는 변경 불가능한 사실을 캡처합니다.
- 이벤트 소비 시 서비스가 적용하는 로직에 관계없이 발생합니다.
- 무기한 대규모로 유지되며 필요한 만큼 사용할 수 있습니다.
출처: https://cloud.google.com/eventarc/docs/event-driven-architectures
이러한 이벤트를 활용한 아키텍처를 EDA(Event Driven Architecture)라고 합니다. 이러한 EDA의 이점은 도메인 간의 느슨한 결합을 유지할 수 있다는 것입니다. 이벤트를 발행하는 도메인과 소비하는 도메인이 서로 독립적으로 백포를 할 수 있는 시스템을 설계할 수 있게 됩니다. 또한 필요하다면 개발 언어와 프레임워크를 통해서도 시스템을 개발할 수 있게 되는 큰 장점이 있습니다.
만약에 이벤트가 없다면?
만약 이벤트가 없다면, 개발자는 다른 방법으로 도메인 간의 통신을 해야합니다. 단일 모듈로 구성된 소규모 프로젝트라면 딱히 문제 될 게 없습니다. 그냥 api를 활용하면 되니까요. 그런데, 만약에 서비스의 규모가 커지고 고도화되면서 다양한 기능이 추가될 경우 어떻게 될까요? 아래 이미지를 참고해 봅시다.
이 이미지는 링크드인의 카프카를 만들기 이전에 구성된 아키텍처의 구조 일부입니다. 물론, 위 이미지와 완전히 정확하진 않겠지만, 만약 이러한 형태를 api로만 해결하려고 하면, 굉장히 번거로워질 겁니다.
위 이미지를 기준으로 하나의 기능을 모든 서버들이 호환할 수 있는 형태의 api를 제공하려면 6 x 6 만큼의 경우의 수를 호환시킬 수 있어야 합니다.
따라서, 이렇게 크게 증가한 복잡도를 해소하려면, 다양한 서버가 주고받는 통신을 중간에서 쌓아놓고 분배해 주는 ‘브로커’가 필요합니다.
브로커의 도입
만약에 위의 이미지에 브로커가 도입되면 다양한 서버 간의 다양한 통신은 한층 더 수월해집니다.
시각적으로 더 편안해진 느낌도 있지만, 다음과 같은 이점을 확보할 수 있습니다.
- 기능을 제공해 주는 개체와 소비하는 개체, 그리고 분배하는 개체를 구분하여, 단일 책임 원칙에 따른 이점을 챙길 수 있습니다.
- 기능 분배 시, Broker의 표준 하나만을 지키면 되기 때문에 구현의 복잡도가 비교적 낮아집니다.
그런데, 이러한 브로커를 구체적으로 구현하려면 어떻게 해야 할까요? 다행히, 이러한 구현을 수행할 수 있는 명확한 아키텍처가 있습니다. 소비자 생산자 패턴을 기준으로 한 큐 시스템과 pub/sub 구조가 대표적인 예입니다.
이 글에서 두 가지 내용을 설명하기에는 내용이 너무 길어질 수 있으니, 내용이 잘 정리된 블로그를 공유드릴 테니, 혹시 의미를 모르시는 분은 아래 url을 참고해 주세요.
생산자 소비자 패턴 : https://coding-food-court.tistory.com/81
pub/sub 구조: https://cloud.google.com/solutions/event-driven-architecture-pubsub?hl=ko
이렇게 명확한 레퍼런스가 있다고 해도 이 레퍼런스를 기준으로 브로커를 직접 구현하는 것은 너무 어렵습니다. 다행히도 이러한 패턴과 구조를 바탕으로 아주 잘 만들어진 오픈소스가 있어. 개발자는 이 오픈소스를 잘 활용하면 됩니다. 대표적으로 RabbitMQ와 Kafka가 있습니다.
Kafka vs RabbitMQ
- 둘 다 데이터 스트림 처리에 사용할 수 있는 메시지 대기열 시스템입니다.
- 여기서 데이터 스트림은 고속 처리가 필요한 대용량의 연속적인 증분 데이터입니다.
- RabbitMQ는 여러 소스에서 스트리밍 데이터를 수집하고 처리를 하기 위해 다른 대상으로 라우팅 하는 분산 메시지 브로커입니다..
- Apache Kafka는 실시간 데이터 파이프라인 및 스트리밍 애플리케이션을 구축하는 데 사용되는 스트리밍 플랫폼입니다..
대략적으로 정리해 보면 위와 같을 수 있는데요. 둘 다 메시지를 분배하는 역할을 가지기에 언뜻 보면 다 비슷해 보이지만, 두 오픈소스는 이벤트 메시지를 관리하고 처리하는 방식이 다르기에 상황에 따라 적절한 선택이 중요합니다.
두 오픈소스의 차이는 다음과 같습니다.
RabbitMQ vs Kafka
아키텍처 | RabbitMQ의 아키텍처는 복잡한 메시지 라우팅을 위해 설계되었습니다. 푸시 모델을 사용합니다. 생산자는 다른 규칙을 사용하여 소비자에게 메시지를 보냅니다. | Kafka는 처리량이 많은 스트림을 실시간으로 처리하기 위해 파티션 기반 설계를 사용합니다. 풀 모델을 사용합니다. 생산자는 소비자가 구독하는 토픽과 파티션에 메시지를 게시합니다. |
메시지 처리 | RabbitMQ 브로커는 메시지 사용을 모니터링합니다. 메시지가 사용되면 RabbitMQ 브로커는 사용된 메시지를 삭제합니다. RabbitMQ 브로커는 메시지 우선 순위를 지원합니다. | 소비자는 오프셋 트래커를 사용하여 메시지 검색을 추적합니다. Kafka는 보존 정책에 따라 메시지를 보관합니다. 메시지 우선 순위가 없습니다. |
성능 | RabbitMQ는 지연 시간이 짧습니다. 초당 수천 개의 메시지를 전송합니다. | Kafka는 초당 최대 수백만 개의 메시지를 실시간으로 전송합니다. |
프로그래밍 언어 및 프로토콜 | RabbitMQ는 광범위한 언어 및 레거시 프로토콜을 지원합니다. | Kafka는 프로그래밍 언어 선택의 폭이 제한적입니다. TCP를 통한 바이너리 프로토콜을 데이터 전송에 사용합니다. |
위 차이점을 바탕으로 Kafka와 RabbitMQ의 사용 시기를 살펴보겠습니다.
Kafka와 RabbitMQ의 사용 시기
RabbitMQ와 Kafka는 서로 경쟁하는 메시지 브로커가 아닙니다. RabbitMQ와 Kafka는 둘 중 하나가 더 적합한 여러 사용 사례에서 데이터 교환을 지원하도록 설계되었습니다.
다음으로, RabbitMQ 및 Kafka와 관련하여 고려해야 할 몇 가지 사용 사례를 살펴보겠습니다.
이벤트 스트림 재생
- Kafka는 수신된 데이터를 다시 분석해야 하는 애플리케이션에 적합합니다. 보존 기간 내에 스트리밍 데이터를 여러 번 처리할 수도 있고 로그 파일을 수집하여 분석할 수도 있습니다.
- RabbitMQ의 경우 사용된 메시지는 삭제되기 때문에 로그를 집계하기는 더 어렵습니다. 해결 방법은 생산자가 저장한 메시지를 재생하는 겁니다.
실시간 데이터 처리
- Kafka는 메시지를 스트리밍 할 때 지연 시간이 매우 짧기 때문에 스트리밍 데이터를 실시간으로 분석하는 데 적합합니다. 예를 들어 kafka를 분산 모니터링 서비스로 사용하여 온라인 트랜잭션 처리 알림을 실시간으로 생성할 수 있습니다.
복잡한 라우팅 아키텍처
- RabbitMQ는 요구 사항이 모호하거나 라우팅 시나리오가 복잡한 클라이언트에게 유연성을 제공합니다.
- 예를 들어 바인딩과 교환이 서로 다른 애플리케이션으로 데이터를 라우팅 하도록 RabbitMQ를 설정할 수 있습니다.
효과적인 메시지 전달
- RabbitMQ는 푸시 모델을 적용합니다.
- 즉, 생산자는 클라이언트 애플리케이션이 메시지를 사용했는지 여부를 알 수 있습니다.
- 데이터를 교환하고 분석할 때 특정 순서 및 전달 보장을 준수해야 하는 애플리케이션에 적합합니다.
결론
결국 이벤트가 유행하는 이유는 서비스의 규모 및 사용자의 다양한 요구사항에 따라 증가하는 아키텍처의 구현 복잡도를 단순화시킬 수 있는 보편적인 수단이기 때문입니다.
즉, 애플리케이션의 처리량을 높이는 것보다는 복잡도를 줄이는 것이 더 큰 목적이며, 오픈소스를 활용하면 일련의 복잡한 비즈니스 로직의 일부를 비동기로 전환시킴으로써, 불필요한 응답시간이 줄어드는 것뿐입니다.
따라서, 단일 모듈에서 요청 처리 기준으로는 오히려 이벤트를 활용하는 것은 오히려 오버엔지니어링에 해당합니다.
그러니 개발자는 상황에 따라 이벤트를 활용여부를 결정해야 하고, 이벤트를 활용하지 않더라도 확장성을 고려하여, 느슨하고 유연한 클린 코드를 유지하여 이벤트가 필요한 순간에 적재적소에 확장할 수 있어야 합니다.
요즘에는 거의 rabbitMQ 혹은 kafka를 사용할 줄 알아야 하는 경우가 많습니다. 그러나, 이 두 기술 중 하나 혹은 혼용해야 한다는 것은 결국 별도의 ‘브로커 서버’가 추가되는 것이고 서버가 추가된다는 것은 네트워크 통신 횟수가 늘어난다는 뜻입니다. 따라서, 만약에 ‘이벤트’ 프로그래밍을 왜 해야 하고, 선택에 따른 트레이드오프를 계산할 줄 모른다면, 오히려 서비스의 응답시간을 늘리는 요소가 될 수 있습니다.
참고
RabbitMQ와 Kafka - 메시지 대기열 시스템 간의 차이점 - AWS
RabbitMQ는 간단한 아키텍처로 복잡한 메시지 라우팅을 제공하는 반면, Kafka는 애플리케이션이 스트림 기록의 데이터를 처리할 수 있을 만큼 내구성이 우수한 메시지 브로커 시스템을 제공합니다.
aws.amazon.com
https://aws.amazon.com/ko/what-is/streaming-data/
스트리밍 데이터란 무엇인가요? - 스트리밍 데이터 설명 - AWS
스트리밍 데이터는 짧은 대기 시간으로 처리하는 것을 목표로, 계속해서 증분하는 방식으로 내보내지는 대용량 데이터입니다. 조직은 보통 몇 바이트부터 메가바이트(MB)에 이르는 다양한 크기
aws.amazon.com