Day 3. Kafka 핵심 구조
1. 오늘의 학습 목표
- Kafka에서 Broker, Cluster, Topic의 관계를 설명할 수 있다.
- Partition이 저장·순서·병렬 처리에 미치는 영향을 설명할 수 있다.
- Consumer Group과 Offset의 역할을 구분할 수 있다.
- Record의 Key가 Partition 선택과 순서 보장에 왜 중요한지 설명할 수 있다.
- Partition 수와 Consumer 수의 관계를 판단할 수 있다.
2. 학습 전 생각해보기
질문 1
하나의 주문 이벤트 목록을 여러 Stock 인스턴스가 함께 처리하면서도 같은 이벤트를 중복 분담하지 않으려면 무엇이 필요할까?
내 답변
고유한 처리 id가 존재하여 처리id가 중복되면 무시하는 방식의 처리 로직이 필요하다
질문 1 정석 답변 보기
여러 Stock 인스턴스를 같은 Consumer Group에 포함해야 한다. Kafka는 같은 Consumer Group 안에서 하나의 Partition을 한 시점에 하나의 Consumer에만 할당한다. 따라서 같은 Partition의 이벤트를 여러 Stock 인스턴스가 동시에 나누어 처리하지 않도록 조정할 수 있다. 예를 들어 Topic에 Partition이 3개 있고 Stock Consumer가 3개라면, 각 Consumer가 서로 다른 Partition을 하나씩 담당할 수 있다. 다만 Consumer Group은 정상적인 병렬 분담을 관리하는 장치이지, 장애로 인한 메시지 재전달까지 완전히 막아 주는 장치는 아니다. 처리 성공 후 Offset을 기록하기 전에 Consumer가 종료되면 같은 이벤트가 다시 전달될 수 있다. 따라서 작성한 답변처럼 eventId를 이용한 멱등 처리도 별도로 필요하다. 정리하면 Consumer Group은 여러 인스턴스의 작업 분담을 담당하고, eventId를 이용한 멱등성은 재전달로 인한 중복 업무 효과를 방지한다.질문 2
주문 A의 생성과 취소 순서는 지켜야 하지만 주문 A와 B는 동시에 처리해도 된다. 메시지를 어떻게 나누면 좋을까?
내 답변
A와 B를 서로 다른 파티션에 할당하여 병렬로 처리하게 하되 같은 주문끼리는 동일한 파티션에 배정받게 하여 순서상 꼬이는 일 없이 조정해줘야한다.
질문 2 정석 답변 보기
Record의 Key로 orderId를 사용하면 된다. Kafka Producer는 같은 Key를 가진 Record를 같은 Partition으로 보낼 수 있다. 따라서 주문 A의 생성과 취소 이벤트에 모두 A의 orderId를 Key로 사용하면 두 이벤트가 같은 Partition에 저장된다. Kafka는 같은 Partition 안에서 Record의 순서를 보장하므로 주문 A의 생성과 취소 순서를 유지할 수 있다. 주문 B는 다른 orderId를 Key로 사용한다. 주문 A와 B는 서로 다른 Partition에 배정될 수 있으므로 여러 Consumer가 병렬로 처리할 수 있다. 중요한 것은 A와 B가 반드시 서로 다른 Partition으로 배정된다고 보장되는 것은 아니라는 점이다. 서로 다른 Key도 Partition 수와 분배 결과에 따라 같은 Partition에 들어갈 수 있다. 핵심은 같은 주문의 이벤트가 같은 Partition으로 가도록 만드는 것이다.질문 3
Consumer가 재시작했을 때 어디까지 처리했는지 어떻게 알 수 있을까?
내 답변
처리된 offset을 기록하여 다시 시작할 offset부터 다시 진행하면 된다.
질문 3 정석 답변 보기
Kafka는 Consumer Group별로 각 Partition에서 어디까지 처리했는지를 Offset으로 관리한다. Consumer가 이벤트 처리를 성공한 뒤 해당 Offset을 Commit하면, 재시작한 Consumer는 Consumer Group에 기록된 Offset을 기준으로 다음 Record부터 처리를 이어갈 수 있다. 예를 들어 Partition 0에서 Offset 10까지 성공적으로 처리하고 그 위치를 Commit했다면, 재시작 후에는 일반적으로 그다음 위치부터 읽는다. 다만 메시지를 읽은 시점과 업무 처리가 성공한 시점은 다르다. 업무 처리가 끝나기 전에 Offset을 먼저 Commit하면, 처리 도중 장애가 발생했을 때 해당 메시지가 누락될 수 있다. 반대로 업무 처리는 성공했지만 Offset Commit 전에 장애가 발생하면 같은 메시지가 다시 전달될 수 있다. 따라서 단순히 “읽은 Offset”이 아니라 성공적으로 처리한 위치를 적절한 시점에 Commit하는 것이 중요하다.3. 핵심 개념 설명
3.1 Kafka의 역할, Broker와 Cluster
Kafka는 이벤트 Record를 지속적으로 저장하고 여러 Producer와 Consumer 사이에서 전달하는 분산 이벤트 스트리밍 플랫폼이다. 단순히 메모리에서 전달하고 버리는 것이 아니라, 설정된 기간 동안 Record를 로그 형태로 보관한다.
Broker는 Kafka 서버 한 대를 뜻한다. 여러 Broker가 모여 Cluster를 구성하고 Topic의 데이터를 나누어 보관한다. 일부 서버에 문제가 생겨도 데이터를 계속 제공할 수 있도록 복제할 수 있다.
오늘은 Kafka가 메시지를 어떤 구조로 나누고 소비 위치를 어떻게 관리하는지에 집중한다. 설치 설정과 Spring Kafka 코드는 다루지 않는다.
3.2 Topic과 Partition
Topic은 같은 종류의 Record를 모으는 논리적 이름이다. 예를 들어 주문 관련 사건을 특정 Topic에 기록할 수 있다. Producer는 Topic을 지정해 Record를 보낸다.
Topic은 하나 이상의 Partition으로 나뉜다. Partition은 Record가 추가되는 순서 있는 로그다. 새 Record는 Partition 끝에 붙고, 각 위치에 번호가 부여된다.
Partition을 여러 개 두면 서로 다른 Partition을 여러 Consumer가 병렬로 처리할 수 있다. 반면 Partition 수와 배치 방식이 시스템의 병렬성과 순서 보장 범위를 결정하므로 무작정 늘리는 것은 아니다.
3.3 Producer와 Record의 Key·Value
Producer는 Topic에 Record를 보낸다. Record의 Value에는 이벤트 본문이 담기며, Key에는 보통 주문 ID나 상품 ID처럼 관련 Record를 묶는 기준을 둔다.
Kafka Producer는 Key를 이용해 Partition을 선택할 수 있다. 같은 Key가 같은 Partition으로 가도록 하면 동일 주문의 생성·취소 이벤트가 한 로그에 기록되어 순서를 유지하기 쉽다.
Key가 없거나 적절하지 않으면 같은 업무 대상의 이벤트가 서로 다른 Partition으로 갈 수 있다. Kafka가 Topic 전체의 절대 순서를 보장한다고 가정하면 안 된다.
3.4 Consumer, Consumer Group
Consumer는 Partition에서 Record를 읽어 처리한다. 같은 Consumer Group에 속한 Consumer들은 Partition을 나누어 맡는다. 한 Partition은 한 시점에 같은 Group의 Consumer 하나에만 할당된다.
예를 들어 Partition이 3개이고 Stock Consumer가 같은 Group에 3개라면 각각 하나씩 맡아 병렬 처리할 수 있다. Consumer가 5개라면 2개는 할당받을 Partition이 없어 대기한다.
다른 Group은 같은 Topic을 독립적으로 읽을 수 있다. Stock Group과 Analytics Group은 같은 이벤트를 각각 자기 목적에 맞게 처리한다.
3.5 Offset
Offset은 Partition 안에서 Record의 위치를 나타내는 번호다. Consumer Group은 자신이 어디까지 처리했는지에 해당하는 위치를 관리한다. Consumer가 재시작하면 이 정보를 기준으로 읽기를 이어갈 수 있다.
Record를 읽었다는 사실과 업무 처리가 성공했다는 사실은 다르다. 처리 완료 전후 어느 시점에 Offset을 확정하느냐에 따라 누락이나 중복 가능성이 달라진다.
이후 학습: Offset 처리와 관련된 중복 전달, at-least-once, 멱등성은 Day 4에서 다룬다.
3.6 메시지 순서 보장 범위
Kafka가 보장하는 기본 순서는 같은 Partition 내부의 기록 순서다. Partition 0의 Record와 Partition 1의 Record 중 어느 것이 먼저 소비될지는 하나의 전체 순서로 보장되지 않는다.
따라서 순서를 지켜야 하는 같은 업무 대상은 같은 Key로 같은 Partition에 보내는 설계가 중요하다. 주문 ID를 Key로 사용하면 한 주문의 이벤트 순서를 보존하면서 서로 다른 주문은 여러 Partition에서 병렬 처리할 수 있다.
같은 Partition이라도 Consumer 애플리케이션이 Record를 내부 스레드에 다시 병렬 분배하면 처리 완료 순서를 흐트러뜨릴 수 있다. Kafka의 저장 순서와 애플리케이션의 처리 완료 순서는 구분해야 한다.
4. 흐름으로 이해하기
Producer
│ key=order-42, value=OrderCreated
↓
Topic: order-events
├── Partition 0: [offset 0] [offset 1] ...
├── Partition 1: [order-42: offset 0] [order-42: offset 1] ...
└── Partition 2: [offset 0] [offset 1] ...
↓ ↓ ↓
Consumer A Consumer B Consumer C
같은 Consumer Group
Key는 Record가 들어갈 Partition을 정하는 기준이 될 수 있다. 각 Partition은 독립된 순서 로그이며, 같은 Group의 Consumer들이 Partition을 나누어 처리한다. Offset은 각 Partition에서 읽은 위치를 나타낸다.
5. 직접 채우는 핵심 질문
질문 1. Broker와 Cluster의 관계를 설명하라.
내 답변
브로커는 kafka 서버 한대를 의미한다. Cluster는 이러한 서버가 여러대 운용중인것을 말하는것 같도 Topic을 나누어 저장하기도하고 한 서버에서 장애가 발생하면 복제해서 운용하는 식인 것 같다.
질문 1 정석 답변 보기
Broker는 Kafka를 실행하는 서버 한 대 또는 하나의 Kafka 서버 프로세스를 의미한다. Cluster는 여러 Broker가 모여 하나의 Kafka 시스템으로 동작하는 구조다. Topic의 Partition들은 여러 Broker에 분산되어 저장되므로 저장 공간과 처리 부하를 나눌 수 있다. 또한 Partition의 복제본을 다른 Broker에 미리 저장할 수 있다. 현재 Partition을 담당하는 Broker에 장애가 발생하면 다른 Broker에 저장된 복제본이 역할을 이어받을 수 있다. 장애가 발생한 뒤 처음 복제하는 것이 아니라, 평소에 복제본을 유지하다가 장애가 발생하면 다른 복제본으로 전환한다는 점이 중요하다.답변에 포함해야 할 키워드
- Kafka 서버
- 여러 Broker
- 분산
질문 2. Topic과 Partition은 각각 무엇을 나타내는가?
내 답변
Topic은 저장하고자 하는 상태? 일련의 주제에 가까운 것 같다. 주문 생성, 주문 취소 와 같은 것들이 하나의 토픽이고, 다른 종류의 카테고리 들은 다른 Topic으로 묶이는 듯 싶다. Partition은 이 각각의 토픽에 대해 저장할수 있는 큐를 여러개 나누어 둔 것이다.
질문 2 정석 답변 보기
Topic은 같은 종류의 이벤트를 논리적으로 분류하는 이름이다. 예를 들어 주문과 관련된 이벤트를 order.events Topic에 저장할 수 있다. OrderCreated와 OrderCancelled를 반드시 서로 다른 Topic으로 만들어야 하는 것은 아니다. 프로젝트의 이벤트 설계에 따라 두 이벤트를 주문 관련 Topic 하나에 함께 저장할 수도 있다. Partition은 하나의 Topic을 여러 개의 물리적인 로그로 분할한 것이다. 각 Partition에는 Record가 순서대로 끝에 추가되며, Record마다 Offset이 부여된다. Partition은 일반적인 Queue와 비슷하게 느껴질 수 있지만 완전히 같지는 않다. Consumer가 Record를 읽어도 즉시 삭제되지 않으며, Kafka의 보관 설정에 따라 일정 기간 유지된다. 서로 다른 Consumer Group은 같은 Record를 각각 독립적으로 읽을 수 있다.답변에 포함해야 할 키워드
- 논리적 분류
- 로그
- 분할
질문 3. Kafka가 Topic 전체의 순서를 보장한다고 말하면 왜 틀린가?
내 답변
Topic 단위로는 순서를 보장하지 않는다. Topic에는 여러 Partition들이 존재하기 때문에 Partition 간 순서는 지켜지지 않는다. 하지만 Partition 내부 순서는 보장되기 떄문에 순서가 중요한 트랜잭션은 id나 해시코드를 활용하여 같은 partition에 배정하는 것이 중요하다 .
질문 3 정석 답변 보기
Kafka가 기본적으로 보장하는 Record의 순서는 같은 Partition 내부의 기록 순서다. 하나의 Topic에 여러 Partition이 있으면 각 Partition은 독립적으로 Record를 저장하고 Consumer에게 전달한다. Partition 0과 Partition 1은 서로 다른 Consumer가 동시에 처리할 수 있으므로 두 Partition 사이의 전체 순서는 보장되지 않는다. 따라서 같은 주문의 이벤트 순서를 지켜야 한다면 orderId를 Record Key로 사용할 수 있다. Kafka Producer는 Key를 기준으로 Partition을 선택하므로 같은 orderId를 가진 이벤트를 같은 Partition으로 보낼 수 있다. 이렇게 하면 동일 주문의 순서를 유지하면서 서로 다른 주문은 여러 Partition에서 병렬로 처리할 수 있다.답변에 포함해야 할 키워드
- Partition 내부
- 전체 순서
- 병렬 처리
질문 4. 주문 ID를 Record Key로 사용할 때 얻는 이점을 설명하라.
내 답변
일단 고유하기 떄문에 멱등성 처리에 유리하고 같은 주문ID는 같은 partition으로 배정되게 구현할수 있기 때문에 순서 보장이 필요한 구조에서 활용하기 쉽다.
질문 4 정석 답변 보기
주문 ID를 Record Key로 사용하면 동일한 주문에 속하는 이벤트를 같은 Partition으로 보낼 수 있다. 예를 들어 주문 100번의 OrderCreated, OrderPaid, OrderCancelled 이벤트가 모두 orderId=100을 Key로 사용하면 같은 Partition에 기록될 수 있다. Kafka는 같은 Partition 내부의 기록 순서를 보장하므로 동일 주문의 상태 전이 순서를 유지하기 쉬워진다. 다만 orderId와 eventId는 목적이 다르다. 하나의 주문에서는 여러 이벤트가 발생하므로 같은 orderId가 반복해서 사용된다. 따라서 orderId만으로는 동일 이벤트의 중복 여부를 정확하게 판단하기 어렵다. Partition 선택과 주문별 순서 보장에는 orderId를 사용하고, 중복 이벤트를 식별하는 멱등 처리에는 별도의 고유한 eventId를 사용하는 것이 더 명확하다.답변에 포함해야 할 키워드
- 같은 Partition
- 동일 주문
- 순서
질문 5. Partition이 4개이고 같은 Group의 Consumer가 2개라면 어떻게 분담할 수 있는가?
내 답변
잘 모르겠긴한데 Consumer가 보통 하나의 Partition을 맡는다고 들은거같아서 나머지 Partition 2개는 대기중이지 않을까..?
질문 5 정석 답변 보기
Consumer 하나는 여러 Partition을 담당할 수 있다. Partition이 4개이고 같은 Consumer Group에 Consumer가 2개 있다면, Kafka는 일반적으로 각 Consumer에게 2개 정도의 Partition을 할당할 수 있다. 예를 들면 다음과 같이 분담할 수 있다. Consumer A: Partition 0, Partition 1 Consumer B: Partition 2, Partition 3 같은 Consumer Group 안에서 하나의 Partition은 한 시점에 하나의 Consumer만 담당한다. 하지만 하나의 Consumer가 반드시 Partition 하나만 담당해야 하는 것은 아니다. 따라서 이 상황에서는 4개의 Partition이 모두 처리 대상이 되며, 두 Consumer가 Partition을 나누어 병렬로 처리한다.답변에 포함해야 할 키워드
- Partition 할당
- Consumer당 여러 Partition
- 병렬성
질문 6. Partition이 2개인데 같은 Group의 Consumer를 5개로 늘리면 처리 병렬성이 5가 되는가?
내 답변
윗 질문에 대한 연장인데 .. 아마 거꾸로 안듯 싶다. Partition 기준으로 하나의 Partition에는 하나의 Consumer가 붙고, Consumer는 Partition을 여러개 맡아도 되는 구조일듯 싶다. 따라서 이 질문에 대한 대답은 Partition이 2개이므로 Consumer또한 2개로 제한되고 나머지 셋은 대기상태가 된다.
질문 6 정석 답변 보기
처리 병렬성이 5가 되지 않는다. 같은 Consumer Group 안에서 하나의 Partition은 한 시점에 하나의 Consumer에만 할당된다. Partition이 2개라면 동시에 Partition을 할당받아 처리할 수 있는 Consumer는 최대 2개다. Consumer가 5개라면 2개는 각각 Partition 하나를 담당하고, 나머지 3개는 할당받을 Partition이 없어서 대기하게 된다. 따라서 같은 Consumer Group의 유효한 Kafka 소비 병렬성은 기본적으로 Partition 수를 초과할 수 없다. Consumer 수만 늘려서는 Partition 수 이상의 병렬 처리 효과를 얻을 수 없다.답변에 포함해야 할 키워드
- 최대 2
- 미할당 Consumer
- Partition 수
질문 7. Stock Group과 Analytics Group이 같은 Topic을 각각 읽을 수 있는 이유는 무엇인가?
내 답변
잘 모르겠는데 아마 topic 내에 partition이 여러개 있어서 topic은 같지만 서로 다른 partition을 읽는 구조이지 않을까?
질문 7 정석 답변 보기
Stock Group과 Analytics Group은 서로 다른 Consumer Group이기 때문에 같은 Topic의 이벤트를 각각 독립적으로 읽을 수 있다. Kafka는 Consumer Group별로 Offset을 따로 관리한다. 따라서 Stock Group이 어떤 Record를 읽었다고 해서 Analytics Group의 읽기 위치가 함께 변경되지 않는다. 예를 들어 order.events Topic에 100개의 Record가 있다면 Stock Group과 Analytics Group은 각각 자신의 처리 위치를 관리하면서 동일한 100개 Record를 모두 읽을 수 있다. Partition을 나누어 처리하는 것은 같은 Consumer Group 내부의 Consumer들 사이에서 일어난다. 서로 다른 Group이 Topic의 서로 다른 Partition만 나누어 읽는다는 뜻은 아니다. 각 Group은 Topic 전체를 독립적으로 소비한다.답변에 포함해야 할 키워드
- 서로 다른 Group
- 독립 소비
- 각자의 Offset
질문 8. Offset이 단순한 이벤트 ID와 다른 점을 설명하라.
내 답변
Offset은 partition 내 위치를 의미하고, 실행 실패를 할 경우 재시작의 기준점이 되기도 한다. 그 말고도 음 … O(1)에 접근 가능한 이점이 있나 ..?
답변에 포함해야 할 키워드
- Partition 내 위치
- Consumer Group
- 재시작
질문 9. Kafka에 기록된 순서와 애플리케이션에서 처리가 끝나는 순서가 다를 수 있는 예를 적어라.
내 답변
기록된 순서와 처리가 끝나는 순서가 다를수 있는 예가 뭐가 있을까 예를 들어 stock 저장완료 이벤트를 보냈는데, db에는 반영이 되었지만, 이벤트 발행에 실패해서 ..? 처리가 안되는 경우? 근데 이건 처리가 끝나는 순서가 달라지는건 아닌데 무슨 상황인지 잘 모르곘다
답변에 포함해야 할 키워드
- 내부 병렬 처리
- 완료 순서
- Partition
6. 상황 판단 문제
상황 1
OrderCreated와 OrderCancelled가 같은 주문에 대해 발행되었지만 Key가 없어 서로 다른 Partition으로 갔다.
- 어떤 순서 문제가 생길 수 있는가?
- Key를 무엇으로 정하는 것이 자연스러운가?
내 답변
OrderCreated 보다 OrderCancelled가 먼저 발행되어서 있지도 않은 주문에 대한 취소로 인해 다른 모듈에서 해당 이벤트르 무시하거나, 처리함으로 인해 취소되어야할 Order에 대한 추후 처리가 무시되거나, 오류를 발생시킬수 있다. 여기서 근데 궁금한건 OrderCreated와 OrderCancelled는 이미 사실이니까 Order 서버에서의 해당 주문?은 취소된게 맞는데 Product나 다른 서버에서의 처리에서 문제를 발생시키는 상황인게 내가 이해한게 맞는 건가?
상황 2
주문 Topic은 Partition이 3개다. Stock 서비스의 Consumer 인스턴스를 3개에서 8개로 늘렸지만 처리량이 늘지 않았다.
- 왜 5개 Consumer가 처리에 참여하지 못할 수 있는가?
- 처리량을 높이기 전에 함께 검토할 것은 무엇인가?
내 답변
Partition 하나당 Consumer 한개, 따라서 처리량은 늘지 않는다. 처리량을 높히기 위해서는 Partition을 늘리고 Consumer가 여러 Partition을 맡게 하면되는데 Partition을 무작정 늘리면 또 문제가 생길거같긴하지만, 정확히 어떤 문제가 생기는지는 잘 모르겠다 ..
상황 3
Consumer가 offset 10의 Record를 읽고 업무 처리 도중 종료되었다.
- 재시작 위치를 결정하려면 어떤 정보가 필요한가?
- Offset 확정 시점이 왜 중요한가?
내 답변
재시작 하기 위해서는 우선 처리중이었던 Topic, Partition에 대한 정보가 필요하고 해당 Partition은 Offset이 어디었는지에 대한 정보가 필요하다. 따라서 Offset 확정시점이 중요하다. 처리가 된 이벤트인지 아닌지에 따라 재시작 시점이 달라지므로 확정 시점이 중요하다. 근데 뭐 .. 확정시점이 좀 달라져서 이미 진행한 이벤트를 또 처리하게 되면 멱등성 처리로 무시하면되는거 아닌가?
7. 오늘의 용어 정리
| 용어 | 내가 작성하는 정의 |
|---|---|
| Kafka | |
| Broker | |
| Cluster | |
| Topic | |
| Partition | |
| Producer | |
| Consumer | |
| Consumer Group | |
| Offset | |
| Record Key / Value |
8. 프로젝트에 한 줄 연결
- 서비스가 발행하는 이벤트 Record는 Kafka Topic에 저장되고 Consumer가 읽는다.
- 주문 ID 같은 Key는 같은 주문의 이벤트를 동일 Partition에 모으는 기준이 될 수 있다.
- Stock Consumer 인스턴스의 유효 병렬성은 Topic의 Partition 수와 관계가 있다.
9. 오늘의 자기 점검
- 핵심 개념을 내 말로 설명할 수 있다.
- 오늘 등장한 주요 용어를 구분할 수 있다.
- 주문 시스템에 개념을 적용할 수 있다.
- 이해하지 못한 부분을 표시했다.
- 다음 주제와 현재 주제를 혼동하지 않는다.
10. 이해하지 못한 부분
11. 3문장 요약
12. 복습 퀴즈
- Kafka 서버 한 대를 무엇이라고 부르는가?
- Kafka가 기본적으로 순서를 보장하는 범위는 어디인가?
- Partition 3개에 같은 Group의 Consumer가 5개면 동시에 할당받을 수 있는 Consumer의 최대 수는 몇 개인가?
- Consumer가 Partition에서 읽은 위치를 무엇이라 하는가?
- 동일 주문의 이벤트를 같은 Partition에 보내기 위해 Record의 무엇을 활용할 수 있는가?
복습 퀴즈 정답과 해설 보기
- Broker다.
- 같은 Partition 내부다. Topic 전체의 절대 순서는 보장되지 않는다.
- 3개다. 같은 Group에서 한 Partition은 한 시점에 Consumer 하나가 맡는다.
- Offset이다.
- Key다. 주문 ID를 Key로 사용하는 식으로 동일 업무 대상을 묶을 수 있다.