🌏 비동기 메시징 시스템이란?
✅ 먼저, 비동기(Asynchronous)란?
- 동기: 요청을 보내고 응답을 받을 때까지 기다림
- 비동기: 요청을 보내고 응답을 기다리지 않고 다른 일 진행
메시징 시스템은 일단 메시지를 보내 두면, 상대가 나중에 처리해도 되도록 하는 구조입니다.
✅ 메시징 시스템이란?
서비스 간에 데이터를 직접 주고받는 대신, 중간에 메시지 브로커라는 우편함을 두는 방식입니다.
- 발신자(Producer)는 메시지를 우편함에 넣고 바로 다른 일 하러 가는 역할
- 수신자(Consumer)는 필요할 때 우편함을 열어 메시지를 읽어 처리하는 역할
이렇게 하면 만약에 수신자 서버가 제대로 동작하지 않는다 하더라도, 메세지 브로커에 할 일을 쌓아두었다가 추후에 서비스가 다시 정상 동작할 때 일을 처리할 수 있으므로 안정적으로 시스템을 운영할 수 있습니다.
✅ 비동기 메세징 시스템의 효능(?)
- 서비스 간 결합도 낮춤 = 느슨한 연결(Loosely Coupled)
- A 서비스가 B 서비스에 직접 HTTP 요청을 보내면, B가 죽으면 A의 요청도 실패하면서 오류가 발생함. (서로 강하게 결합됨)
- 메시징을 쓰면 A는 그냥 메시지를 큐에 넣고 끝이고, B가 살아날 때 처리 가능
- 안정성(장애 격리)
- 한쪽이 터져도 전체 시스템이 같이 죽지 않음
- 메시지가 중간에 안전하게 저장되기 때문에 복구 가능하고, 추후 처리 가능
- 확장성(Scalability)
- 소비자(Consumer)를 여러 개 늘리면 동시에 메시지를 병렬 처리 가능하기 때문에 대량 트래픽 처리에 유리함
- 비동기 처리
- 꼭 지금 처리할 필요 없는 작업(예: 이메일 발송, 로그 저장, 알림 전송)을 나중에 처리하기 때문에 사용자 응답 속도 빨라짐
✅ 사용 예시
주문 시스템을 구현한다고 했을 때,
- 결제 성공 → 메시지 발행
- 메일 서비스가 메시지를 보고 “영수증 발송”
- 포인트 서비스가 메시지를 보고 “포인트 적립”
- 재고 서비스가 메시지를 보고 “재고 차감”
이 모든 작업들을 다 메세징으로 구현하면 독립적으로 구현할 수 있습니다.
✅ 전통적인 메시징 시스템
- RabbitMQ, ActiveMQ, IBM MQ
- 원래부터 "메시지 큐(Message Queue)" 역할을 위해 만들어졌음
- 특징: 메시지를 한 번 소비하면 사라짐 (Queue 성격)
✅ 카프카(Kafka)
- 처음에는 LinkedIn이 "실시간 로그 수집/처리"용으로 만든 시스템
- 메시징 시스템이면서 동시에 분산 로그 저장 시스템이라고도 불림.
- RabbitMQ 같은 큐 방식과 다르게, 메시지를 지우지 않고 일정 기간 저장해 둡니다.
우리는 여러 가지 메세징 시스템이 있지만, 대용량 처리에 특화되고, 분산/확장성이 용이하며 메세지를 로그 파일로 디스크에 저장해 복구가 유연한 ‘카프카’에 대해서 공부해보려고 합니다.
먼저 용어부터 알아봅시당
🌏 카프카에서 사용되는 용어

✅ Producer (생산자)
데이터를 보내는 쪽
예를 들어, 주문 서비스가 "주문 생성됨" 이벤트를 메시지로 발행하면 Producer 역할을 하게 됩니다. Producer는 데이터를 Topic이라는 곳에 써 넣습니다.
[Producer] → "주문 완료" → [Kafka Topic]
✅ Consumer (소비자)
데이터를 받아서 처리하는 쪽
메일 서비스가 "주문 생성됨" 메시지를 보고 영수증 메일을 보내는 경우, Consumer 역할을 하게 됩니다. Consumer는 Topic에서 메시지를 꺼내 읽습니다.
[Kafka Topic] → "주문 완료" → [Consumer]
✅ Broker (브로커)
카프카 서버 자체
한 대의 서버에 모든 메시지를 보관하면 위험하니까, 보통 여러 대의 Broker를 띄워서 클러스터를 만듭니다. 클러스터 안에서 토픽을 분산 저장하고, 메시지를 안전하게 보관합니다.
Kafka Cluster = Broker1 + Broker2 + Broker3 ...
✅ Zookeeper (또는 KRaft)
원래 카프카는 Zookeeper라는 별도 시스템을 통해 클러스터 상태를 관리했습니다.
- 브로커 관리
- 여러 대의 Kafka Broker가 있을 때, 누가 클러스터에 참여했는지 기록
- 새로운 Broker가 들어오거나 나가면 상태 갱신
- 리더 선출
- 각 Partition에는 Leader Broker가 있고, 나머지는 Follower
- 장애가 나면 Zookeeper가 새로운 Leader를 뽑음
- 메타데이터 저장
- 어떤 Topic이 있고, Partition이 몇 개인지
- Consumer Group의 Offset 정보 등
하지만 Zookeeper를 별도로 운영하니 운영이 복잡해지고, 이중 관리 문제 + 성능의 병목이 되면서 KRaft Controller라는 프로세스 역할을 하는 Broker가 있는 KRaft 모드가 생겼습니다
내장 합의 알고리즘(Raft) 도입으로, 최근 버전부터는 KRaft 모드로 Zookeeper 없이 동작 가능합니다.
✅ Topic
메시지를 넣어두는 큰 상자
모든 메시지는 반드시 특정 Topic에 저장됩니다.
- 예: 주문 이벤트는
order-topic, 결제 이벤트는payment-topic
✅ Partition
하나의 Topic을 여러 개 조각(Partition)으로 나눌 수 있습니다.
Partition을 여러 개로 나누면 메시지를 분산 저장하고 동시에 병렬 처리할 수 있습니다.
- 예:
order-topic을 3개의 Partition으로 나눔 → 주문 데이터가 나눠져 저장됨
order-topic
├─ Partition 0 : 주문1, 주문4, 주문7 ...
├─ Partition 1 : 주문2, 주문5, 주문8 ...
└─ Partition 2 : 주문3, 주문6, 주문9 ...
각 Partition은 같은 Consumer Group 내에서는 딱 1개의 Consumer만 처리하도록 해서 중복 처리를 막습니당
✅ Offset
Partition 안에서 메시지의 고유한 번호
Consumer는 Offset을 기준으로 메시지를 읽고, “어디까지 읽었는지” 기록해 둡니다. Offset은 Consumer가 스스로 기억할 수도 있고, Kafka가 내부적으로 저장해 줄 수도 있습니다. (보통은 __consumer_offsets 토픽에 저장)
- 예: Partition 0에서
Offset=5까지 읽었다면, 다음에는Offset=6부터 읽음
✅ Consumer Group
Consumer들을 묶어서 하나의 그룹으로 만듭니다.
각 Partition은 한 그룹 안에서 단 한 Consumer만 처리합니다. 덕분에 메시지가 중복 처리되지 않고, 동시에 여러 Consumer가 나눠서 일을 할 수 있습니다.

(위 그림에서는 1:1이지만) 전체적인 카프카 구조에서 본다면, 토픽(Topic) ↔ 컨슈머 그룹(Consumer Group) 관계는 다 : 다 구조입니다.
1) 하나의 토픽을 여러 Consumer Group이 읽을 수 있음
예를 들어 order-topic에 「주문완료」 라는 메세지가 발행되면,
email service consumer group → 영수증 발송
point service consumer group → 포인트 적립
inventory service consumer group → 재고 차감
이런 식으로 같은 메세지가 각 그룹마다 한번씩 독립적으로 전달되도록 할 수 있습니다 = Pub/Sub 모델
2) 한 Consumer Group은 여러 토픽을 구독할 수도 있음
반대로, Consumer Group 하나가 order-topic, payment-topic 두 개 토픽을 동시에 구독할 수도 있습니다.
🌏 Consumer Group을 통한 병렬 처리
✅ 기본 아이디어
- Consumer Group = 여러 개의 Consumer(소비자)를 하나의 그룹으로 묶은 것
- Kafka는 한 Partition을 동시에 여러 Consumer가 읽을 수 없게 막아두는 대신 Partition 단위로 나눠서 Consumer들에게 배분
⇒ 병렬 처리 가능
✅ 예시 1: Topic에 Partition이 3개, Consumer Group 안에 Consumer 3명인 경우
order-topic (Partition 3개)
├─ Partition 0 → Consumer A
├─ Partition 1 → Consumer B
└─ Partition 2 → Consumer C
- 각 Consumer가 하나의 Partition을 맡아서 읽음 (효율 100%)
- 동시에 3개의 메시지를 병렬로 처리 가능
✅ 예시 2: Topic에 Partition이 3개, Consumer Group 안에 Consumer 2명인 경우
order-topic (Partition 3개)
├─ Partition 0 → Consumer A
├─ Partition 1 → Consumer A
└─ Partition 2 → Consumer B
- Consumer A가 Partition 0,1 두 개 맡고
- Consumer B가 Partition 2를 맡음
여전히 메시지들은 중복 없이 나눠서 처리되지만, 균등 분배는 아님
✅ 예시 3: Topic에 Partition이 3개, Consumer Group 안에 Consumer가 4명인 경우
order-topic (Partition 3개)
├─ Partition 0 → Consumer A
├─ Partition 1 → Consumer B
└─ Partition 2 → Consumer C
Consumer D → 할당받을 Partition 없음 (Idle 상태)
- Partition 수(3) < Consumer 수(4)면, 남는 Consumer는 놀고 있게 됩니다.
정리하자면,
- Partition 수 ≥ Consumer 수일 때, 병렬 처리 극대화
- Partition 수 < Consumer 수일 때 , 일부 Consumer는 할당 못 받고 놀게 됩니다.
✅ 예시 4: Topic 1개에 Consumer Group이 2개인 경우
똑같이 order-topic에 "주문 완료" 메시지가 들어온다고 했을 때, 이번에는 order-service-group과 email-service-group 두 개 그룹이 존재합니다.
두 그룹은 서로 독립적이므로 같은 메시지를 각각 한 번씩 소비하게 됩니다.
Producer → order-topic
Group 1: order-service-group
Partition 0 → Consumer A
Partition 1 → Consumer B
Group 2: email-service-group
Partition 0 → Consumer X
Partition 1 → Consumer Y
결과적으로 Pub/Sub 모델처럼, 같은 메시지가 두 그룹에 모두 전달됩니다.
order-service는 주문 DB 업데이트email-service는 영수증 메일 발송
즉 한번의 publish로 두 번의 처리가 가능해집니다.
🌏 Kafka 효율적 활용 전략 (뽕 뽑기)
✅ 하나의 토픽을 여러 파티션으로 나누기
- 이유: 파티션 수 ≥ Consumer 수일 때, 병렬 처리를 극대화할 수 있음
- 예: Consumer Group에 Consumer 5개가 있으면, 최소 5개 이상의 Partition을 두어야 각자 일을 나눠 가질 수 있음
- 만약에 Partition이 5개 미만이면 Consumer 하나는 놀게 되기 때문에 비효율적
- 파티션 = "작업 분산 단위"
- 주의사항: 파티션을 너무 많이 만들면 관리 비용 증가 (메타데이터 관리, 리밸런스 비용 커짐).
- 보통 트래픽량과 Consumer 수를 고려해서 적당한 파티션 수를 정합니다.
✅ 토픽을 기능별로 분리하기
- 이유: 관심사가 다른 메시지를 한 토픽에 섞으면 Consumer가 불필요한 데이터까지 다 읽어야 함
- 예
- order-topic → 주문 관련 이벤트
- payment-topic → 결제 관련 이벤트
- email-topic → 알림/메일 관련 이벤트
Consumer는 필요한 토픽만 구독해서 처리 효율 ↑
✅ Consumer Group이 여러 토픽을 구독하게 하는건 트레이드오프 고려하기
- 하나의 Consumer가 여러 작업 (토픽 여러 개 구독)
- 장점: 단일 서비스에서 여러 역할 가능해서 운영이 단순해진다
- 단점: 한 Consumer 장애 시 여러 기능이 동시에 멈출 수 있음
- Consumer를 역할별로 분리 (각 서비스 전용 Consumer Group을 만드는 경우)
- 장점: 독립적 확장/장애 격리 가능
- 단점: 운영 복잡도 ↑ (브로커가 많이 필요해질 수 있음)
🌏 참고자료

도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!
'아키텍처+MSA' 카테고리의 다른 글
| [아키텍처/Kafka] 메세지 복제(Replication)와 브로커의 Leader-Follower 구조 (0) | 2025.09.17 |
|---|---|
| [아키텍처/Kafka] Kafka만의 특징: 분산 로그 저장, Queue vs Topic (기존 메세징 시스템과 차이) (1) | 2025.09.17 |
| [아키텍처] 아키텍처 개선과 SPOF 해결: SPOF 문제는 꼬리에 꼬리를 이어.. (3) | 2025.08.18 |
| [아키텍처] 재배포 및 배포 롤백: 서비스 장애 발생 시 피해를 최소화하기 위한 롤백의 중요성 (2) | 2023.08.26 |
| [대규모 시스템 설계] 사용자 수에 따른 규모 확장성(2): 응답 시간 개선을 위한 캐시, 컨텐츠 전송 네트워크(CDN) 도입 (0) | 2023.08.25 |