//
요즘 개발자를 위한 시스템 설계 수업 · 디렌드라 신하, 테자스 초프라
큐를 시스템에 끼워 넣는 이점은 세 가지로 요약된다.
큐를 설계하고 구현할 때 확보해야 하는 것은 확장성, 장애 허용성, 메시지 일관성이다. 그리고 시스템의 주요 구성 요소는 큐 관리자, 메시지 저장소, 로드 밸런싱, 장애 허용과 복구, 확장성이다.
책이 나열하는 항목들은 실무에서 하나씩 걸려 본 적이 있는 것들이다.
| 항목 | 결정해야 하는 것 |
|---|---|
| 메시지 순서 보장 | 전역 순서가 필요한가, 파티션 내부 순서만 필요한가 |
| 데이터 영속성 | 메시지를 얼마나 오래 보관할 것인가 |
| 보안 | 인증, 권한, 전송 구간 암호화 |
| 기술 스택 선택 | 각 분산 큐의 특징과 장점이 다르므로 워크로드에 맞춰 고른다 |
| 메시지 형식 설계 | 스키마와 버전 관리 전략 |
| 장애 허용성 | 재시도 메커니즘, 처리 불가능한 메시지를 위한 데드 레터 큐(DLQ) |
이 목록에서 가장 나중에 후회하는 항목이 메시지 형식 설계다. 큐에 흐르는 메시지는 생성자와 소비자가 독립적으로 배포되는 상황에서 공유되는 계약이다. 처음부터 스키마 버전 필드를 넣고 알 수 없는 필드를 무시하는 규칙을 정해 두지 않으면, 나중에 생성자와 소비자를 같은 시점에 배포해야 하는 상황이 생긴다. 큐를 도입한 이유였던 결합도 감소가 무너지는 순간이다.
생성자가 특정 토픽에 메시지를 발행하면, 그 토픽을 구독한 소비자가 메시지를 받아 보는 구조다.
큐와의 결정적 차이는 하나의 메시지가 여러 소비자에게 전달된다는 점이다. 큐는 보통 하나의 메시지를 하나의 소비자가 가져가지만, 발행/구독은 구독자 수만큼 복제해 전달한다.
주요 특징은 다음과 같다.
이를 갖추기 위해 필요한 구성 요소는 토픽 관리, 메시지 라우팅, 소비자 관리, 서비스 품질 기술, 확장성과 로드 밸런싱이다.
| 요소 | 내용 |
|---|---|
| 수평 확장과 수직 확장 | 브로커 수를 늘리는가, 브로커 성능을 올리는가 |
| 동적 로드 밸런싱 | 메시지 브로커가 여러 구독자 사이에 메시지 부하를 고르게 나눈다 |
| 토픽 파티셔닝 | 하나의 토픽을 작고 관리하기 쉬운 여러 파티션으로 나눈다 |
토픽 파티셔닝이 발행/구독 시스템 확장의 핵심 장치다. 파티션 수가 병렬 처리의 상한을 정한다.
메시지 전달 보장은 세 수준으로 나뉜다.
| 수준 | 의미 | 위험 |
|---|---|---|
| 최대 한 번(At-most-once) | 중복은 없지만 유실될 수 있다 | 메시지 손실 |
| 최소 한 번(At-least-once) | 유실은 없지만 중복될 수 있다 | 중복 처리 |
| 정확히 한 번(Exactly-once) | 유실도 중복도 없다 | 구현 비용과 성능 부담 |
실무에서 가장 흔한 선택은 최소 한 번 + 소비자 측 멱등성(Idempotency) 조합이다. "정확히 한 번"을 브로커 수준에서 보장하려면 트랜잭션 오버헤드가 상당하다. 대신 메시지에 고유 ID를 넣고 소비자가 이미 처리한 ID를 기록해 두면, 중복이 와도 결과가 같아진다. 브로커가 어렵게 해결하는 문제를 애플리케이션이 싸게 우회하는 방법이다.
이 외에 장애 허용성과 메시지 순서 보장이 신뢰성의 축을 이룬다. 소비자 관리와 메시지 라우팅 측면에서는 소비자 등록 및 관리, 효율적인 메시지 라우팅이 필요하다.
아파치 카프카는 대용량 실시간 데이터 피드를 처리하려고 설계된 분산 스트리밍 플랫폼이다. 내구성, 속도, 확장성을 고려해 설계됐고, 발행/구독과 큐 기반 메시지 패턴을 모두 구현할 수 있다는 점이 강점이다.
핵심 개념은 넷이다.
책은 분산 환경에서 카프카를 배포하는 과정을 네 단계로 정리한다.
1. 카프카 클러스터 설정하기
브로커 개수와 각 브로커의 세부 설정을 정한다. 그리고 주키퍼를 통합 설정한다. 카프카는 클러스터 정보를 관리하고, 주키퍼는 브로커 간 역할 분담을 조율하는 데 쓰인다.
2. 토픽 생성 및 설정하기
토픽을 만들 때 파티션 수와 복제 계수(Replication Factor) 를 설정해 성능과 안정성을 조절한다.
3. 카프카 생성자와 소비자 구현하기
4. 모니터링과 유지보수
클러스터 모니터링과 성능 튜닝을 지속한다.
주키퍼 의존성은 카프카 2.8 이후 도입된 KRaft 모드로 제거될 수 있다. KRaft는 3장에서 본 래프트 합의 알고리즘을 카프카 내부에 직접 구현해 메타데이터를 관리하는 방식이다. 운영 컴포넌트가 하나 줄어들고 파티션 수 확장 한계도 완화된다. 새로 클러스터를 구성한다면 KRaft 기반을 먼저 검토하는 편이 낫다.
카프카 스트림은 카프카 위에서 데이터를 처리하려고 설계된 도구다. 별도의 스트림 처리 프레임워크 없이 카프카 내에서 바로 작업을 수행할 수 있다는 점이 특징이다.
| 특징 | 내용 |
|---|---|
| 카프카와 완전한 통합 | 카프카 토픽에서 데이터를 읽고 쓰는 작업이 자연스럽다 |
| 고수준 DSL | map, filter, join, aggregate 등의 연산을 제공한다 |
| 저수준 Processor API | 처리 구조를 직접 설계하고 상태를 관리하며 맞춤형 로직을 구현한다 |
| 상태 기반 처리 | 시간이 지난 후에도 애플리케이션 상태를 유지하고 갱신한다 |
| 정확히 한 번 처리 | 카프카 트랜잭션 기능을 활용해 일관성과 안정성을 유지한다 |
| 확장성 및 장애 허용성 | 수평 확장이 가능하고 파티션을 인스턴스 간에 자동 분배한다 |
스트림 처리 애플리케이션은 토폴로지(Topology) 라는 형태로 정의된다. 프로세스(노드)와 스트림(에지)으로 구성된 그래프다.
AWS가 제공하는 클라우드 기반 스트림 처리 서비스로, 대규모 데이터 스트림을 실시간으로 처리하도록 설계됐다.
| 구성 요소 | 역할 |
|---|---|
| Kinesis Data Streams | 서비스의 중심. 대규모 데이터를 실시간으로 수집하고 처리한다 |
| Kinesis Data Firehose | 스트리밍 데이터를 AWS 서비스나 외부 저장소로 바로 적재한다 |
| Kinesis Data Analytics | 표준 SQL 쿼리로 스트리밍 데이터를 실시간 분석한다 |
카프카와 대응 관계로 보면 이해가 빠르다. Data Streams가 카프카 토픽에 해당하고(카프카의 파티션이 키네시스의 샤드에 대응), Data Analytics가 카프카 스트림에 해당한다. Firehose는 카프카 커넥트가 담당하는 적재 역할에 가깝다. 차이는 운영 부담이다. 키네시스는 브로커를 직접 관리하지 않는 대신 처리량 단위로 과금되고 세부 튜닝의 여지가 적다.
이 장은 발행/구독 시스템과 분산 큐를 비교적 밀도 있게 다룬다. 특히 확장성과 신뢰성을 결정하는 요소를 항목별로 나눠 놓은 부분은 실제로 큐를 도입할 때 체크리스트로 쓸 만하다.
전달 보장의 세 수준과 파티셔닝은 반드시 짚어야 할 개념인데 책도 빼놓지 않았다. 다만 카프카와 키네시스는 개요 수준에서 그친다. 배포 절차를 네 단계로 나열하는 정도라, 실제 운영에서 부딪히는 컨슈머 그룹 리밸런싱이나 오프셋 관리, 파티션 수 결정 같은 문제는 별도 자료가 필요하다.
이 장에서 얻은 가장 유용한 관점은 큐와 발행/구독을 같은 축의 서로 다른 지점으로 보게 된 것이다. 하나의 메시지를 한 소비자가 가져가면 큐이고, 여러 소비자가 각자 가져가면 발행/구독이다. 카프카는 컨슈머 그룹이라는 개념으로 이 둘을 하나의 구조 안에서 모두 표현한다.
이 글이 도움이 되셨나요?