본문 바로가기
Backend/Data Pipeline

[Kafka] 면접 결과 기다리다가 멘탈 나갈 것 같아서 시간 때우기 위해 공부하는 카프카

by persi0815 2026. 6. 18.

0. 개요

시간 때우기 용으로 그동안 궁금했던 Redis Streams가 영향을 받은 카프카에 대해 공부해보고자 한다.
 
'쉬운 코드' 유투브 채널과 카프카 공식 문서 참고했다. 
https://www.youtube.com/@ezcd
https://kafka.apache.org/
 

1. 카프카란? 

카프카 홈페이지에 들어가면 해당 문구가 눈에 들어온다.

 
 

Apache Kafka is an open-source distributed event streaming platform

 
여러 대의 컴퓨터를 네트워크로 연결하여 하나의 거대한 시스템처럼 동작하는 환경에서 사용하는 (distributed)
sw 시스템에서 발생한 어떤 사실을 나타내는 불변의 데이터를 (event)
목적지까지 끊임없이 실시간으로 흐르게 하는 (streaming)
플랫폼 (platform)
 
즉, 카프카를 통해 이벤트들의 스트림을 발행, 구독하여 끊임없이 데이터를 다른 시스템으로 import 혹은 export 할 수 있다. 
+) 스트림을 원하는 만큼 오랫동안 영구 저장할 수 있다. 
+) 일련의 사건들을 발생 즉시 또는 사후에 처리하기 위해 사용할 수 있다. 
 

2. 카프카 탄생 배경

LinkedIn에서 2004년 1M에서 2009년 50M까지 사용자가 크게 늘었다. 
 
뉴스피드, 광고 시스템처럼 사용자 액션으로 트리거가 돼서 비동기로 동작해야 하는 기능이 있었다.
Rest API로 구현하려면 사용자 행동 발생 시 DB를 거치고 뉴스피드나 광고를 갱신하는 로직들을 순차적 동기 방식로 처리가 되는데, 그러면 API의 응답 시간은 크게 늘어나기도 하고, 중간 로직들이 동기적으로 실행되었다. 
 
기존 MQ들은 관리와 확장이 매우 어려웠다. 
링크드인에는 사용자가 많은 만큼 정말 다양한 시스템들이 있었는데, 이들 간의 안정적인 데이터 파이프라인이 필요하다는 요구가 생겼다. 
 
그래서 ad-hoc하게 해결해왔다. 즉, 그때 그때 애플리케이션과 시스템 사이에 파이프라인을 구축해왔다. 
그런데, 서로 다른 시스템 간 파이프라인을 연결하면 할 수록 아래와 같이 점점 더 복잡해졌다. 

https://www.confluent.io/blog/event-streaming-platform-1/

 
이렇게 파이프라인이 복잡해지다 보니 확장성과 가용성, 운영 효율성에 제동이 걸렸고, 메시징 시스템의 신뢰성은 떨어졌다. 
또한, 복잡도가 증가할 수록 신뢰되지 않는 데이터들이 생겨났는데, 이로 인한 레포트 또한 신뢰할 수 없었다.  
 
시스템의 무분별한 확장에 시달린 끝에, 결국 데이터 '스트림 모델링'에 중점을 둔 시스템을 새로 하나를 만들자는 결론에 도달했다. 표준화된 방식으로 모든 시스템과 애플리케이션들에 스트림 형태로 데이터를 전달할 수 있고, 이를 기반으로 풍부한 실시간 애플리케이션까지 구축할 수 있었다. 

https://www.confluent.io/blog/event-streaming-platform-1/

 
카프카가 허브가 되어 중앙에서 범용적인 데이터 파이프라인 역할을 수행한다. 즉, 시스템 간 데이터 흐름의 중추가 되었다. 
카프카를 중추에 두고 필요에 따라 표준화된 파이프라인을 만들고 그 파이프라인을 필요한 앱들이 consume할 수 있도록 구성됐다.
 
표준화된 데이터 파이프라인이 실시간(ms 단위) 스트림으로 동작했고, 카프카가 여러 데이터 센터에 분산된 상태에서 하루 1조 건 이상의 이벤트를 처리할 수 있었다. 
 

3. 장점은 무엇이며, 언제 사용해야 할까? 

장점
- 높은 처리량: 초당 수백만개의 메시지를 처리할 수 있다. 
- 낮은 지연: 메시지 producing부터 consuming까지 수 ms 레벨에서 처리된다. 
- 높은 확장성: 트래픽이 더 늘어나도 쉽게 scale out이 가능하다. 
- 영구적 저장: 데이터를 잃어버리지 않고 오래 보관할 수 있다. Kafa의 성능은 데이터 크기에 관계가 없기에 장기간 저장해도 된다. 
   *다만, topic 별 설정으로 카프카가 event를 보존할 기간을 정의하여야 한다. 이를 넘어서면 삭제된다. 
- 높은 가용성: 일부 장비가 고장나도 죽지 않고 굴러간다. 
 
언제 사용하면 좋을까? 
1. 사용자 액션에 대한 처리로 부터 비동기적으로 동작해야 하는 애플리케이션이 점점 증가하는 경우
2. 서비스가 계속 성장하여 여러 microservice와 시스템들이 생기고 있고 이들 사이의 데이터 파이프라인 또한 계속해서 증가하고 있는 경우
3. 중앙에서 범용적으로 동작하는 표준화된 데이터 파이프라인이 필요한 경우
4. 실시간으로 빠르게 이벤트 처리/분석이 필요한 경우
5. 최근 상태뿐만 아니라 상태의 변화 흐름도 필요한 경우

 
언제 쓰지 말아야 할까? 
1. 트랜잭션으로 묶여야 하는 경우
- Kafka와 DB는 기본적으로 하나의 원자적 트랜잭션으로 묶기 어렵기 때문에, 이벤트 유실이나 불일치 가능성이 있다. 이를 보완하기 위해 Transactional Outbox Pattern과 같은 보완 장치가 필요하다.
2. 복잡한 쿼리/검색이 필요한 경우
- 기본적으로 검색용 db가 아니다. 
3. 소규모의 트래픽이 발생하는 서비스를 운영하는 경우
- 운영/관리 오버헤드, 인프라 비용, 디버깅의 어려움이 있다. 
 

4. 주요 카프카 키워드

L: Leader / F: Follower

 
Kafka Cluster: 카프카가 동작하는 카프카 프로세스(서버) 집합
Broker: kafka cluster를 구성하는 카프카 프로세스(서버) 각각을 의미.
- 운영 환경에서는 서버와 프로세스가 1:1 관계.
- 한 서버에 여러 프로세스를 띄워서 클러스터를 구성할 수도 있다. 
 
*클러스터로 구성하는 이유
- 브로커 추가를 통한 수평 확장성 용이
- 분산/복제를 통한 장애 허용성 확보
 

 
Topic: 메시지가 분류되어 저장되는 주제 혹은 카테고리
- 하나의 카프카에 여러 개의 토픽을 만들 수 있다. 
- producer 와 subscriber는 0~N개가 존재할 수 있다. 
 
Partition: 병렬 처리를 위해 topic은 여러 partition으로 나눠짐. 
- 메시지는 partition들 중에 한 곳에 immutable로 append됨. 즉, partition에 저장되면 임의로 삭제와 수정이 불가능함.
- 각 파티션은 FIFO지만, 소비되는 즉시, 삭제되진 않는 log 성격을 갖음. retention 기간 존재. 
  → 순차적 처리가 필요하다면 동일 파티션에 이벤트를 발행해야 한다. 
- 하나의 메시지는 반드시 한 partition에만 저장. *동일한 내용이라도 메시지가 다르면 여러 파티션에 저장될 수 있음
- 동일한 event key를 가진 event는 동일한 partition에 기록된다. * 키 값 지정하지 않는다면 round robin 방식으로 전송. 
 
Offset: partition 안에 저장된 메시지들의 순서. 이를 바탕으로 다음에 읽어야 할 메시지를 확인한다. 
- 0부터 순차적으로 증가하는 숫자로 구성됨. 
 
Producer: 애플리케이션에서 발생한 이벤트를 topic에 저장. 
- partitioner: 이벤트가 어떤 partition에 저장되어야 하는지를 결정. 
 
Consumer: topic에 저장된 이벤트를 consume하여 목적에 맞게 처리
- offset을 통해 어디까지 consume했는지를 추적. 
- 레코드(데이터) 받을 때 순서를 보장하지 않음. 
 
Redis Streams와 같이, producers와 consumers는 완벽히 decoupled되어 있다. 즉, 서로 신경 쓰지 않고, 각자의 이벤트 발행과 소비라는 역할에만 집중하면 된다. 

.
Consumer Group: 동일한 목적으로 묶인 consumer들의 그룹
- Topic 구독의 단위
- 같은 group에 속한 consumer들은 partition들을 나눠 맡아 메시지 처리
- offset 어디까지 읽었는지 관리는 consumer group 단위로 이뤄짐. 
- 서로 다른 consumer group은 같은 topic도 독립적으로 consume가능! 즉, consumer group에서 독립적으로 offset 관리!
- 모든 consumer는 반드시 단 하나의 consumer group에 속해야 함. 
- partition 수 >= consumer 수 권장. 

  • partition 수 == consumer 수 : consumer group b처럼 로드 밸런싱
  • partition 수 > consumer 수: consumer group a처럼 브로드캐스트
  • partition 수 < consumer 수: 동작하지 않고 대기하는 consumer 발생 
https://my-develop-note.tistory.com/266#google_vignette

 
Event, Message, Record는 모두 같은 대상(“something happened”)을 가리키지만, 강조하는 관점이 다르다. 
- Event: 비즈니스 관점. source application에서 발생한 일 혹은 사실임을 강조
- Message: 전달 관점. producer에서 consumer로 전달된다는 것을 강조. 
- Record: 카프카 내부 관점. 카프카에서 저장되는 데이터 그 자체를 강조. 
 
Log: 하나의 파티션에 순서대로 append되는 immutable한 메시지 흐름. (stream)
Segment: log를 실제로 저장하기 위해 여러 개의 파일로 나눈 단위.
*Segment 파일 구성: .log, .index, .timeindex 이 세개의 파일이 하나의 segment. 파일의 이름은 파일의 첫 메시지의 offset. 
 
Segment가 필요한 이유 (즉, log를 여러 파일로 나눠서 관리해야 하는 이유)
- log를 하나의 파일로 관리하면 파일 크기가 너무 커져서 관리가 어려움. 
- 일정 크기(기본 1gb)나 시가(기본 7일) 단위로 새로운 segment 생성
- active segment(현재 쓰고 있는 segment)만 append 가능(나머지 segment들은 immutable)
- log는 segment 단위로 삭제되며, 특정 시간이나 사이즈 이상일 때 가장 오래된 segment 부터 삭제됨. 
 
 
Replication & Leader & Follower & ISR
- Replication: 각 'partition'의 데이터를 여러 브로커에 복제하여 일부 브로커에 문제가 생겨도 메시지 유실없이 정상적으로 kafka가 동작하도록 하는 기능
- Leader: partition에 produce/consume 요청을 처리하는 브로커. 한 partition 당 하나만 존재. 
- Follower: Leader로부터 partition을 복제해서 저장하고 있는 브로커. 계속 polling으로 복제해와서 sync를 맞춘다. 
- Replica: partition의 원본과 복제본을 가지고 있는 브로커들 전체. Leader + Follower
- ISR(In-Sync Replicas): 복제가 잘 되고 있어서 Leader와 동기화 상태인 replicas. Leader 장애 시 여기서 새로운 Leader 선출!
- replication-factor: topic 생성할 떄 원본 포함 복제본 몇 개 다루 것인지 설정. 보통 3을 추천하며 broker 수 보다 클 순 없다. 
 
Zookeeper: kafka라는 분산 시스템을 안정적이고 일관성 있게 유지하는 데 필요한 메타데이터를 관리하고 조정 작업을 수행. 
- 운영 복잡성, 스케일링 등등 여러 이슈들로 은퇴
KRaft: Zookeeper 역할을 Kafka cluster 내부에서 일부 노드들이 수행. 
- 더 이상 Zookeeper cluster 따로 구축하고 관리할 필요가 사라짐.