Kafka는 메시지를 생산하고 소비하는 주체와 그 메시지를 분류하고 저장하는 공간으로 구성된다
- 프로듀서 (Producer): Kafka로 메시지(데이터)를 생성하여 전달하는 주체이다. 이들은 특정 “토픽”으로 메시지를 발행한다
- 컨슈머 (Consumer): Kafka에 저장된 메시지(데이터)를 구독하고 처리하는 주체이다. 컨슈머는 자신에게 필요한 토픽의 메시지를 가져와 실제 비즈니스 로직을 수행한다
- 토픽 (Topic): Kafka에 저장될 메시지의 종류를 구분하는 논리적인 카테고리이다. 메시지는 반드시 특정 토픽으로 분류되어 저장된다
Kafka의 기본적인 데이터 흐름
- 프로듀서의 메시지 발생: 프로듀서 서버는 특정 종류의 작업을 처리하기 위한 정보를 담은 메시지를 생성한다. 이때, 이 메시지가 어떤 종류의 정보인지를 나타내기 위해 email.send, order.created 등과 같은 토픽 이름을 저장하여 Kafka에게 전달한다. 프로듀서는 Kafka에게 “이 메시지를 email.send 토픽에 저장해줘”라고 요청하는 역할은 한다
- Kafka의 메시지 저장: Kafka는 프로듀서로부터 전달받은 메시지를 지정된 토픽에 따라 분류하여 임시 저장소에 보관한다. 메시지는 단순히 저장될 뿐, 전통적인 메시지 큐와는 다르게 컨슈머가 메시지를 읽더라도 큐에서 제거되지 않는다
- 컨슈머의 메시지 소비: 컨슈머 서버는 Kafka에 새로운 메시지가 있는지 주기적으로 확인(폴링)하다가, 자신에게 할당된 토픽에 새로운 메시지가 도착하면 이를 조회하여 가져온다. 컨슈머는 가져온 메시지를 바탕으로 이메일 발송, 주문 처리, 사용자 비밀번호 변경 처리 등 실제 비즈니스 로직을 수행한다
이러한 분리된 역할 덕분에 프로듀서와 컨슈머는 서로의 처리 속도에 영향을 받지 않고 비동기적으로 동작할 수 있으며, 이는 시스템의 확장성과 안정성을 크게 향상시킨다
CLI를 활용한 Kafka 조작
Kafka의 강력한 기능 중 하나는 백엔드 애플리케이션뿐만 아니라 CLI(Command Line Interface) 터미널을 통해서도 모든 기능을 조작할 수 있다는 점이다. 토픽 생성, 조회, 삭제는 개발 과정에서 자주 사용되는 기능이므로 CLI 명령어를 통해 익혀두면 유용하다
Kafka 설치 디렉토리 (kafka_2.13-4.0.0 또는 유사한 경로)에서 다음 명령어를 실행한다
토픽 생성하기
토픽을 생성할 때는 kafka-topics.sh 스크립트를 사용하며, Kafka 서버 주소와 생성할 토픽 이름을 지정한다
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic email.send
- –bootstrap-server localhost:9092: Kafka 서버의 주소와 포트를 지정한다
- –create: 새로운 토픽을 생성하라는 옵션이다
- –topic email.send: 생성할 토픽의 이름을 email.send로 지정한다
명령어 실행 후 Created topic email.send. 메시지가 출력되면 성공적으로 토픽이 생성된 것이다
토픽 조회하기
생성된 토픽 목록을 조회하거나 특정 토픽의 세부 정보를 확인할 수 있다
모든 토픽 목록 조회
bin/kafka-topics.sh --bootstrap-server localhost:9092 --list
email.send를 포함하여 현재 Kafka에 존재하는 모든 토픽 목록이 출력된다
토픽 세부 정보 조회
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic email.send
–describe 옵션과 –topic 옵션을 함께 사용하여 특정 토픽 (email.send)의 파티션 수, 리플리케이션 팩터 등 상세 정보가 출력된다
토픽 삭제하기
필요 없는 토픽은 다음 명령어로 삭제할 수 있다
bin/kafka-topics.sh --bootstrap-server localhost:9092 --delete --topic email.send
–delete 옵션을 사용하여 email.send 토픽을 삭제한다. 삭제 후 –list 명령어로 다시 확인해보면 토픽이 목록에서 사라진 것을 확인할 수 있다
CLI를 활용한 Kafka 메시지 송수신
Kafka에 메시지를 넣은 프로듀서 역할과 메시지를 읽는 컨슈머 역할을 CLI 터미널을 통해 직접 수행해볼 수 있다
Kafka에 메시지 넣기 (Producer 역할)
먼저, email.send 토픽이 삭제되었다면 다시 생성한다 (삭제 명령어를 위에서 입력했다면)
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic email.send
이제 kafka-console.producer.sh 스크립트를 사용하여 메시지를 전송한다. Kafka 메시지는 키-값 (Kye-Value)형태로 전송될 수 있지만, 여기서는 키를 생략하고 값(Value)만 전송한다
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic email.send
명령어 실행 후 커서가 >로 바뀌면 메시지를 입력할 수 있다
> hello1 > hello2 > hello3
메시지를 입력하고 엔터를 누르면 해당 메시지가 email.send 토픽으로 전송된다. 입력이 끝나면 Ctrl+C를 눌러 프로듀서 세션을 종료한다

Kafka 메시지 조회하기 (Consumer 역할)
Kafka의 중요한 특징 중 하나는 전통적인 메시지 큐(예: RabbiMQ, SQS)와 달리 메시지를 읽어도 큐에서 제거되지 않는다는 점이다. 이는 메시지를 여러 번 읽을 수 있게 하여 다양한 애플리케이션에서 동일한 데이터를 처리할 수 있도록 하는 장점이다
kafka-console.consumer.sh 스크립트를 사용하여 email.send 토픽의 메시지를 조회한다
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic email.send --from-beginning
- –from-beginning: 토픽에 저장된 가장 오래된 메시지부터 현재까지의 모든 메시지를 읽어오라는 옵션이다
명령어를 실행하면 hello1, hello2, hello3 메시지가 순서대로 출력될 것이다. 더 나아가, 이 컨슈머 세션은 실시간으로 새로 들어오는 메시지도 계속해서 읽는다. 새로운 터미널 창을 열어 Kafka 설치 디렉토리로 이동한 후, 다시 프로듀서를 실행하고 메시지를 보내본다.
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic email.send
> 프롬프트에 hello4를 입력하고 엔터를 누르면, 컨슈머가 실행 중인 원래 터미널 창에 hello4가 즉시 출력되는 것을 확인할 수 있다. 이는 Kafka가 메시지를 읽어도 제거하지 않으며, 실시간 스트리밍 처리가 가능하다는 것을 보여준다


메시지 처리의 정교화: Consumer Group과 Offset
실제 서비스 환경에서는 메시지를 단순히 한 번 읽고 끝나는 것이 아니라, 여러 컨슈머가 협력하여 메시지를 처리하고, 어디까지 읽었는지를 기억하여 중복 처리 없이 다음 메시지부터 처리하는 메커니즘이 필요하다. Kafka는 이를 위해 컨슈머 그룹 (Consumer Group)과 오프셋 (Offset) 개념을 제공한다
- 컨슈머 그룹 (Consumer Group): 하나 이상의 컨슈머를 논리적으로 묶는 단위이다. 같은 컨슈머 그룹에 속한 컨슈머들은 한 토픽의 메시지를 서로 분담하여 처리한다
- 오프셋 (Offset): 토픽 내에서 각 메시지의 순서를 나타내는 고유 번호이다. 오프셋은 0부터 시작하며, 메시지가 토픽에 추가될 때마다 순차적으로 증가한다
Consumer Group과 Offset의 동작 방식
Kafka의 각 토픽은 여러 개의 파티션으로 나뉨으로써 병렬 처리 성능을 높일 수 있다. 중요한 것은, 컨슈머 그룹 내의 각 컨슈머는 토픽의 특정 파티션을 전담하여 메시지를 소비하고, 자신이 어디까지 메시지를 읽었는지에 대한 정보를 오프셋 번호로 Kafka에 기록한다. 이 기록을 통해 컨슈머 그룹은 다음 번에 메시지를 읽을 때, 이미 처리한 메시지를 건너뛰고 아직 읽지 않은 메시지부터 순차적으로 처리할 수 있다
Current Offset의 의미: 컨슈머 그룹이 다음에 읽어야 할 메시지의 오프셋 번호를 나타낸다
컨슈머 그룹을 지정하여 메시지 읽기
–group 옵션을 사용하여 컨슈머 그룹을 지정해 메시지를 읽어보자
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic email.send --from-beginning --group email-send-group
- –group email-send-group: email-send-group 이라는 이름의 컨슈머 그룹을 지정한다. 만약 해당 이름의 컨슈머 그룹이 존재하지 않으면 새로 생성한다.
- –from-beginning: 이 옵션은 –group 옵션과 함께 사용될 때 특별하게 동작한다
- 오프셋 기록이 없는 경우: 컨슈머 그룹이 해당 토픽에 대해 읽은 오프셋 기록이 전혀 없다면, from-beginning 옵션에 따라 가장 처음 메시지부터 읽는다
- 오프셋 기록이 존재하는 경우: 컨슈머 그룹이 이미 읽은 오프셋 정보가 있다면, from-beginning 옵션을 무시하고 마지막으로 읽은 오프셋 다음부터 메시지를 읽는다
명령어를 실행하면 hello1, hello2, hello3, hello4 메시지가 모두 출력될 것이다. 이는 email-send-group 이라는 컨슈머 그룹이 처음 생성되어 이전에 읽은 기록이 없었기 때문에 –from-beginning 옵션에 따라 모든 메시지를 읽은 것이다

컨슈머 그룹 조회 및 세부 정보 확인
컨슈머 그룹이 제대로 생성되었고 오프셋 정보가 기록되었는지 확인한다
모든 컨슈머 그룹 목록 조회
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
email-send-group이 목록에 출력되는 것을 확인할 수 있다

특정 컨슈머 그룹 세부 정보 조회
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group email-send-group --describe
출력 결과는 다음과 유사할 것이다
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID HOST CLIENT-ID email-send-group email.send 0 4 4 0 - - -
여기서 주목할 부분은 CURRENT-OFFSET 이다. CURRENT-OFFSET이 4로 되어 있다면, 이는 email-send-group 컨슈머 그룹이 오프셋 0, 1, 2, 3번 메시지까지 이미 읽었으며, 다음에 읽어야 할 메시지의 오프셋 번호를 4번임을 의미한다

컨슈머 그룹의 중복 처리 방지
이제 email.send 토픽에 새로운 메시지를 추가하고, email-send-group이 이전에 읽은 메시지를 건너뛰고 새 메시지만 읽는지 확인한다
새 메시지 추가 (프로듀서)
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic email.send > hello5 Ctrl+C
컨슈머 그룹으로 다시 메시지 읽기
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic email.send --group email-send-group --from-beginning
이번에는 hello1 부터 hello4까지의 메시지는 출력되지 않고, 새로 추가된 hello5 메시지만 출력될 것이다. 이는 email-send-group이 이전에 읽은 메시지를 기억하고 (CURRENT-OFFSET 덕분), 아직 읽지 않은 hello5 메시지부터 처리했음을 보여준다

컨슈머 그룹 세부 정보 다시 확인
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group email-send-group --describe
이제 CURRENT-OFFSET이 5로 변경된 것을 확인할 수 있다. 이는 email-send-group이 오프셋 0부터 4까지의 메시지를 모두 읽었으며, 다음에 읽을 메시지의 오프셋이 5임을 의미한다
