programming language/Kakfa

[Kafka 활용] Kafka 심화 1 - Uber System Design와 Kafka 사용

공대키메라 2026. 7. 4. 00:13

지난 글에서 카프카에서 KRaft에 대해 간단하게 이해하고 실습 진행 및 사용사례를 조사했다.

([Kafka 기초 학습] Kafka 기초개념 7 - KRaft 간단 이해 및 실습 진행 + 사용 사례 조사)

 

이번 시리즈 부터에서는 키메라 스스로가 "Kafka 활용"이라고 명명했다.

 

이유는 실제로 이 kafka가 큰 시스템에서 바라보았을 때 어느 문제점을 해결하기 위해서 어느곳에 배치해야하는지 맥락을 파악하기 위함이다.

 

따지고 보면 이전 글에서 사용 사례를 보면서 뭔가 부족해서 좀 더 활용적인 면에서 어떻게 Kafka를 써야하는지 머리에 집어놓고 싶어서 그렇다.

 

해당 글은 1번 섹션에서 설명하겠지만 Hello Interview 라는 youtuber의 영상을 주로 참고해서 필자의 말로 풀어낸 것이다.

 

다만, 중간 중간에 드는 가려운 부분을 스스로의 효놈손으로 긁어줄 것이다 벅벅!


1.  Kafka Use Cases & Deep Dives (kafka 요약)

Kafka는 생각보다 개념적으로 알아야 할 것이 너무 많고 더럽게 어렵다.

 

하나하나 깊게 파보면 내용은 많은데 너무 단편적으로 접근하다보니 큰 그림이 그려지지 않앗다.

 

고로, Kafka에 대한 요약과 실제 어디서 적용할지의 큰 토대를 다음 링크의 영상들을 통해 다시! 학습했다.

 

Kafka에 대해 이미 잘 아는 사람이라면 이 부분을 건너뛰어도 된다.

 

링크

 

 

아잉 재미있어! Hello Interview Hello!

 

추천하는 카프카 시스템 설계 심층 분석 영상에서 Kafka에 대해서 전체적으로 아울러준다. 

 

위 영상에서 기본 개념보다는 Use case와 Deep Dives 부분을 어떻게 설명해주는지 궁금해서 잘 참고해서 보았다.

 

무엇보다도 많은 양을 깔끔하게 핵심만 짚어서 정리해주기 때문에 학습에 수월하니 관심이 있다면 시청하길 바란다.

 

물론 공식문서에서 Use Cases에 대해 설명해주고 있으니 이를 읽어보는것도 좋다.

 

개인적으로 영상이 좀 더 설명이 풍부하고 와닿았기에 공식문서 말고 영상을 기준으로 Use Case와 Deep Dives를 요약할 것이다.

 

Use Case 정리

1) 언제든지 메시지 큐를 사용하고 싶을 때

 

1.1) 비동기 처리를 하고 싶을 때 (예시: 유튜브 영상 변환시)

메시지 큐를 사용하면 가장 큰 이유는 시스템간의 결합도들 낮추고(decoupling) 더 나은 서비스를 제공하기 위함이다.

즉, 사용자는 일을 던져놓고 다른일을 하면 되는 것이다. (비동기로 작동하니까!)

 

예를 들어, 사용자가 유튜브에 영상을 올린다고 해보자.

여기서 업로드를 하는것으로 그치는게 아니라, 올린 영상을 다른 사용자들도 시청할 수 있도록 변환을 해야 한다.

 

이러한 상황에서는 S3를 사용한다면 PreSigned URL을 통해 직접 영상을 올리고, 다른 기기에서도 작동할 수 있도록 영상을 다양한 화질로 변환해야한다.(transcode) 

 

이러한 상황에서 기존의 Message Queue의 역할을 Kafka가 할 수 있다. 

메시지 큐로서의 역할 흐름 정리

 

Kafka에 message를 넘길 때는 S3의 url을 보내주면 transcoder가 이를 보고 변환작업을 하고 있으면, 사용자는 영상 변환(Transcoding)이 끝날 때까지 화면에서 대기할 필요 없이, 즉각적인 응답(예: HTTP 202 Accepted)을 받고 다른 영상을 시청하거나 브라우저를 벗어나는 등 본인의 다른 작업을 이어나갈 수 있다."

 

1.2) 순서대로 메시지 처리를 하고 싶을 때 (예시: 티켓마스터)

Kafka에서 순서를 보장해서 메시지 혹은 레코드 처리가 가능하다. 어떻게? 파티션 단위로 잘 메시지를 분배해서 말이다.

 

예시로 티켓마스터라고 옆에 적었는데, 어느 경기의 좌석에 대해서 자리를 판매한다고 하자.

 

여러명의 사람들이 특정 좌석을 예약하려고 하면 먼저 시도한 사람이 구매해야한다. 

 

그러면 동시성 문제가 있는데, Kafka는 특정 데이터(예: 특정 좌석)에 대한 예약 요청들을 메시지 키(Message Key)를 이용해 단일 파티션(Partition)으로 해싱(Hashing)하여 할당함으로써, 해당 좌석에 대한 처리 순서를 엄격하게 보장하고 동시성 문제를 해결한다.

 

 

그런데 Kafka로 복잡하게 Queue 용으로 사용하기보다는 개인적으로는 Redis가 낫지 않을까 하는 생각이 든다. 

 

1.3) 프로듀서와 컨슈머를 디커플링 처리해 독립적으로 작동시킴 (예시: LeetCode or Online Judge)

 

 

Leetcode를 예로 들어보자. 

 

Leetcode에서는 Weekly Contest 를 매주 시행하는데, 전 세계에서 수많은 개발자들이 코팅테스트에 참가하는데

 

각자 요청하고 결과를 받으려면 어떻게 하는게 좋을까?

 

web에서 코드를 작성하고 실제 코드가 올바르게 작동하는지는 내부 서버에서 관리해야 할 것이다.

 

이러한 경우 vertical scaling을 통해 많은 사용자의 요청을 받아서 DB에 저장하고 처리해야할 정보를 Kafka로 밀어넣는다.

 

그리고 Worker에게 이를 처리하도록 한다. 많은 worker가 필요하다면 그대로 worker, 즉 consumer를 늘리면 된다.

 

언어에 따른 Runtime들은 비용에 민감해서 이를 늘리기에는 곤란하니 consumer를 통해 kafka의 메시지를 처리하고 요청을 보낸다.

 

 

2. Stream이 필요할 때

2.1) 실시간으로 많은 데이터를 처리할 때

 

실시간으로 많은 데이터를 처리해야 할 때가 있다. 

예를 들어, 광고 클릭수 집계기의 경우, 사용자의 움직임을 실시간으로 분석하기 위해 Kafka stream을 사용하면 좋다고 한다.

 

 

2.2) 동시에 다수의 컨슈머가 메시지 stream을 처리해야 할 때

 

Kafka가 좋은 점은 메시지를 여러 Consumer Group에서 읽을 수 있다는 점이다. 

 

예를 들어서 현재 서울의 날씨 관련 정보를 Kafka로 부터 읽는다 하자.

서울의 지역, 구역에 따라서 partition이 나뉘어 있는데 A 서비스와 B서비스 그리고 나중에 늘어날 수 도 있는 서비스들이 

같은 메시지를 동시에 읽을 수 있다.

 

Deep Dives 정리 - Kafka 사용시 고려사항

1. 확장성(Scalability)

Kafka 사용시에는 메시지 하나당 크기는 1MB가 한계이다.

그리고 하나의 브로커는 1TB의 데이터만 담도록 하고 초당 10k개의 메시지를 처리하는것이 보수적이고 안정적이다.

 

최신 데이터만 브로커의 고속 로컬 디스크에 짧게 유지하고, 오래된 데이터는 저렴하고 무한히 확장 가능한 원격 스토리지(S3 등)로 오프로딩(Offloading)한다.

 

이를 활용하면 로컬 디스크 용량을 작게 유지하여 장애 복구 시간을 획기적으로 단축하면서도, 브로커당 수십 TB 이상의 데이터를 서비스할 수 있다.

 

만약 scale up 하고 싶다면 어떻게 해야할까?

 

단순하다! broker를 늘리면 된다. 그리고 올바른 partition key를 선택하면 된다. 

 

만약 hot key가 발생한다면?

(1) key를 없앤다(?)

키를 없애면 round robin으로 파티션에 골고루 들어가기 때문이다. 순서가 상관없는 경우 key를 없애는것도 하나의 방법이다.

 

(2) 복합키(compound key)를 사용한다. (예: Ad:N or Ad:userId)

N을 이제... 랜덤한 수로 넣어주게 된다면 N에 맞는 파티션으로 분배되어서 들어간다. 

 

 

2. 장애허용 및 내구성(Fault Tolerance & Durability)

 

  • replication.factor=3 (인프라 분산) 데이터를 3대의 서버에 복제하여 물리적 장애(데이터센터 다운 등)에 대비하는 구조적 기반입니다.

 

  • acks=all (데이터 무손실) 동기화된 모든 복제본이 저장을 완료해야 프로듀서에게 성공 응답을 보내 데이터 유실을 원천 차단합니다.

 

  • min.insync.replicas=2 (서비스 가용성) 최소 2대의 서버만 정상이어도 쓰기를 허용하여, 서버 1대 장애나 롤링 배포 시에도 시스템 마비를 방지합니다.

3곳에 복제하고(3), 모두의 저장을 확인하되(all), 최소 2곳만 응답하면 통과(2)시켜 서버 1대의 장애를 허용하면서도 데이터 유실률 0%를 달성하는 방어 구조라고 한다.

 

컨슈머 그룹(Consumer Group) 내에서 3개의 컨슈머 중 1개가 종료되거나 장애가 발생하면, Kafka는 할당을 잃은 파티션들을 남은 2개의 컨슈머에게 다시 분배하기 위해 컨슈머 리밸런싱(Consumer Rebalancing)을 수행한다.

 

3. 에러와 재시도 (Error & Retries)

 

그림을 보면 Main Topic이 실패하는 경우 Consumer 가 Retry Topic으로 메시지를 발행한다. 그리고 그마서도 실패하면 DLQ Topic으로 발행하고 재시도를 한다. 

 

Consumer가 어찌보면 Producer의 역할도 겸임을 하는 것이다. 

 

해당 방법은 특정 메시지 하나가 실패했다고 해서 파티션에 쌓인 뒤의 수만 개의 정상적인 메시지 처리가 지연되는 현상(Head-of-Line Blocking)을 완벽히 차단한다.

 

4. 성능 최적화 (Performance Optimization)

성능 최적화를 위해서는 batch.sized와 linger.ms 설정을 확인하면서 테스트해야한다.

 

5. 보유 정책(Retention Policies)

retention.ms와 retention.bytes는 각각 얼마나 오랫동안 메시지를 가지고 있을 건인지, size가 넘어서면 언제부터 과거 것을 치울건지 설정한다.

 

2. 기존의 Messaging Queue와 Kafka 사용시의 차이점 비교

의문이 들엇는데 Kafka를 굳이 안쓰고도 사실 메시징 큐를 구현할 수 있다.

 

Kafka말고 Rabbit MQ도 많이 들어봤는데, 기존의 messaging queue는 어떠한 문제가 있길래 Kafka를 도입하는 경우가 생기는걸까?

 

이 점이 궁금해서 AI 를 활용해서 정리한다.

 

핵심 아키텍처 차이 비교

구분 전통적인 MQ(예: Rabbit MQ) Kafka
아키텍처 패러다임 Smart Broker / Dumb Consumer Dumb Broker / Smart Consumer
데이터 저장 구조 큐(Queue) 분산 파일 기반 추가형 로그(Append-Only Log)
데이터 전달 방식 브로커가 컨슈머에게 밀어냄(Push) 컨슈머가 브로커에서 당겨옴(Pull)
상태 관리 주체 브로커(메시지 전송 및 ACK 상태 추적) 컨슈머 (자신이 어디까지 읽었는지 오프센 직접 통제)
메시지 생명주기 컨슈머의 처리 완료(ACK)즉시 삭제 소비 여부와 무관하게 설정 기간 (Retention) 동안 보존

 

구조 및 성능 비교

구분 전통적인 MQ(예: Rabbit MQ) Kafka
데이터 보존 소비 완료(ACK) 즉시 삭제 설정된 기간(예: 7일) 동안 유지 (영속성)
라우팅 구조 Exchange, Binding 등 복잡한 라우팅 지원 Topic과 Partition 기반의 단순한 구조
처리량 (Throughput) 초당 수만 건 수준 초당 수십만 ~ 수백만 건 이상 (Zero-copy, 순차 I/O 활용)
확장성 수직적 확장(Scale-up)에 유리, 클러스터링 복잡함 파티션 기반의 완벽한 수평적 확장(Scale-out)
순서 보장 다수의 컨슈머가 붙을 경우 엄격한 순서 보장 어려움 단일 파티션 내에서는 완벽한 순서 보장

 

 

뭐... 그렇다고 한다.

 

3. System Design - Uber System Design

우버를 시스템 디자인 해보도록 하자.

 

우버는 승차 공유 플랫폼으로 개인과 승차 서비스 제공자를 이어준다. 

 

기능적 요구사항(Functional Requirements)과 비기능적 요구사항(Non-Functional Requirements)을 간단하게 알려준다.

 

 

기능적 요구사항

  • 예상 요금 조회: 사용자는 출발지와 목적지를 입력하여 예상 요금을 산출받을 수 있어야 합니다.
  • 차량 호출 요청: 사용자는 산출된 예상 요금을 바탕으로 차량(Ride)을 요청할 수 있어야 합니다.
  • 요청 처리 및 경로 안내: 기사는 수신된 호출 요청을 수락하거나 거절할 수 있어야 하며, 승객의 픽업(승차) 및 드롭오프(하차) 지점까지의 경로를 안내받아야 합니다.

 

 

 

비기능적 요구사항

  • 짧은 지연 시간 (Low Latency Matching): 매칭 프로세스는 지연을 최소화하여, 1분 이내에 매칭 성공 또는 실패 결과를 즉시 반환해야 합니다.
  • 매칭 정합성 보장 (Consistency of Matching): 승객의 호출(Ride)과 기사(Driver)의 매칭은 반드시 1:1로만 이루어져야 합니다. (동일한 호출에 두 명의 기사가 배차되거나, 한 기사가 동시에 두 개의 호출을 수락하는 동시성 문제가 발생해서는 안 됩니다.)
  • 부분적 고가용성 확보 (Highly Available Outside the Matching): 1:1 정합성이 엄격하게 요구되는 매칭 코어 영역(강한 일관성 필요)을 제외한 나머지 시스템 영역은 어떠한 상황에서도 멈추지 않고 높은 가용성을 유지해야 합니다.
  • 대용량 트래픽 및 스파이크 대응 (High Throughput & Surge Handling): 출퇴근 피크 시간대나 특별한 이벤트 발생 시, 특정 지역에 집중되는 수십만 건의 동시다발적인 요청(Surge)을 시스템 다운 없이 안정적으로 처리할 수 있어야 합니다.

 

 

그리고 그린 구성도이다.

 

 

여기서 Kafka를 도입할 수 있는 부분은 두 부분이다.

 

3.1) API Gateway -> Ride Matching Service 사이

API Gateway -> Ride Matching Service로 사용자가 운전 배차를 요청하는 경우 한번에 너무 많은 요청이 가게 되면 timeout이 발생할 수 있다. 

 

가령, 콘서트가 끝나고 수만건의 콜이 한번에 오게 되면 아무래도 Location DB에서 데이터도 조회하고, 분산락도 처리하고, 외부에 푸시 알람도 보내는데 갑자기 요청이 너무 많이 들어오게 된다면 스레드풀이 순식간이 고갈될 것이다. 

 

수평적 확장을 염두해서 아무리 Ride Matching Service에 auto scaling을 적용해도 아무리 짧아도 수 분의 시간이 걸리기에 대초에 Kafka를 도입하게 되면 확장 지연 시간 동안 쏟아지는 트래픽을 유실없이 담을 수 있다.

 

또한, Kafka는 backpressure로 Ride Matching Service가 알아서 소비할 수 있도록 조절 밸브의 역할을 해주니 현재 Uber의 System Design 에서 이를 도입하면 좋을 것이다.

 

3.2) Notification Service

알림 영역에 Kafka를 도입하는 것은 "우리가 통제할 수 없는 외부 네트워크(FCM/APN)의 상태와 무관하게, 우리 시스템의 가장 핵심적인 도메인 로직(매칭)을 100%의 가용성으로 보호하기 위한 가장 훌륭한 방어벽"을 세우는 것이다!

 


필자는 최근에 Youtube에서 Hello Interview라는 System Design 영상을 즐겨 보고 있다. 

 

해당 영상들에 빠진 이유는 글의 초입에서 설명한 것과 크게 다르지 않다. 

 

소프트웨어는 유기적으로 움직이는데 하나 하나의 기술로만 접근을 하려다보니 그 기술에만 집중하다보면 큰 그림을 보지 못하고 기술에만 매몰되는 실수를 범한다. 

 

예를 들어, 개발로 업을 시작한 초기에는 Spring을 너무 열심히 공부한 나머지 Spring만 최고고 무조건 코드적으로 항상 옳은 방식이 있다고 믿었다.

 

그러나 현실은... 늘 녹록치 않는법! 항상 타협해야 하는 부분도 있지만, 무엇보다도 문제가 생길 지점을 거시적인 관점에서 파악하지 못하면 대응이 늦는다고 판단했다.

 

그렇기에 이렇게 Kafka 심화라고 했지만, 사실 좀 엉뚱해 보이지만 숲을 보는 개발자가 되기 위한 하나의 여정으로서 System Design 정리를 먼저 진행했다. 

진실되게 고하자면 kafka 아 그래서 어떻게 어디서 쓰는데가 가장 중요한게 아닌가?

 

아 그래서 어떻게 트래픽도 파악하냐고! 하는 것이 나의 큰 관심사이다. 적당히 해가면서 맞추는게 사실 중요하지않나...

 

다음 시간에는 kafka 책을 토대로 Grafana와 Promethenus 를 연결해서 모니터링 할 수 있는지 알아보겠다.