보물찾기 엔진의 이벤트 스트리밍에 잘못 베팅했다
Source: Dev.to
우리가 실제로 해결하려던 문제
돌이켜 보면, 우리는 단순히 이벤트를 처리하는 수준을 넘어선 다면적인 문제를 해결하려고 했습니다. 플레이어가 오프라인이거나 네트워크 지연이 발생했을 때도 게임 상태가 항상 최신인지 보장해야 했고, 게임 상태를 조작하려는 플레이어를 감지하고 처벌함으로써 치팅을 방지해야 했습니다. 게다가 시스템이 수천 명의 동시 플레이어와 초당 수백만 건의 이벤트를 처리할 수 있도록 확장성을 확보해야 했습니다.
우리는 이벤트 기반 아키텍처가 정답이라는 것을 알고 있었지만, 올바른 이벤트 스트리밍 플랫폼을 선택할 때 발생하는 트레이드오프를 충분히 이해하지 못했습니다.
초기에 우리는 인기와 활발한 커뮤니티 지원 때문에 Apache Kafka를 이벤트 스트리밍 플랫폼으로 선택했습니다. 그러나 곧 Kafka의 고정된 한계, 예를 들어 높은 지연 시간과 제한된 토픽 파티션 기능 때문에 문제에 부딪혔습니다. 시스템은 지속적으로 5초 정도의 지연을 보였고, 이는 전체 게임 경험을 크게 저해했습니다.
우리는 Kafka 설정 파라미터를 조정해 문제를 해결하려 했지만, 결국 무의미한 싸움이었습니다. 또한 고처리량 배치 작업을 감당하지 못하는 Kafka 때문에 주기적인 크래시도 발생했습니다.
몇 주간의 시도 끝에 우리는 Apache Pulsar와 Redis를 조합하는 방향으로 전환했습니다. 플레이어 업데이트, 게임 상태 변화, 채팅 메시지 등 게임의 다양한 측면에 대해 여러 개의 이벤트 스트림을 만들었습니다. 이를 통해 각 컴포넌트를 분리하고 독립적으로 확장할 수 있었으며, 전체 시스템 지연 시간을 크게 줄일 수 있었습니다.
또한 Redis 캐시 레이어를 도입해 게임 상태를 저장함으로써 데이터베이스 부하를 감소시키고 이벤트 스트림에서 데이터를 조회하는 시간을 최소화했습니다. Redis를 캐시 레이어로 사용하면서 캐시된 데이터의 TTL(Time‑to‑Live)을 1초로 설정했으며, 이를 통해 네트워크 지연이 발생하더라도 플레이어에게 최신 게임 상태를 제공할 수 있었습니다.
Apache Pulsar와 Redis로 전환한 결과, 시스템 지연 시간이 5초에서 50밀리초 이하로 크게 감소했습니다. 우리의 이벤트 스트리밍 플랫폼은 초당 1천만 건의 이벤트를 처리할 수 있었고, Redis 캐시 레이어는 수백만 개의 게임 상태 레코드를 캐시할 수 있었습니다.
아마도 더 중요한 점은, 시스템 충돌과 다운타임이 크게 감소했다는 것입니다. 이는 시스템 신뢰성을 유지하는 데 결정적인 역할을 했습니다.
돌이켜 보면, 이벤트 스트리밍 플랫폼을 선택할 때 트레이드오프를 더 깊이 이해하는 데 시간을 투자했어야 했습니다. Apache Kafka는 내결함성 및 고처리량 배치 처리와 같은 강점이 있지만, 모든 애플리케이션에 최적의 선택은 아닙니다.
또한 Redis에 정착하기 전에 Memcached나 Amazon ElastiCache와 같은 대안 캐시 솔루션을 검토했어야 했습니다. 더불어 고처리량 워크로드를 시뮬레이션하고 다양한 상황에서 이벤트 스트리밍 플랫폼의 성능을 검증할 수 있는 보다 견고한 테스트 프레임워크를 구축했어야 했습니다.
궁극적으로 이번 경험을 통해 얻은 핵심 교훈은, 이벤트‑드리븐 아키텍처에 정답은 없으며 각 애플리케이션의 구체적인 요구사항과 제약조건을 신중히 고려해야 한다는 점입니다.