[Apache Kafka] 대용량 트래픽 처리를 위한 카프카 사용법

현대의 데이터 처리 환경에서 정보의 양은 기하급수적으로 증가하고 있습니다. 특히 실시간 데이터 스트리밍이 중요해지면서, 기업들은 데이터의 흐름을 효과적으로 관리할 수 있는 혁신적인 솔루션을 찾고 있습니다.
이 과정에서 Apache Kafka가 주목받고 있는 이유는 무엇일까요? 바로 ‘대용량 트래픽을 처리하는 뛰어난 성능’입니다.
예를 들어, 소셜 미디어 플랫폼에서는 수천만 명의 사용자가 생성하는 데이터를 실시간으로 수집하고 분석해야 합니다. 이러한 환경에서 전통적인 데이터 처리 방식은 한계에 부딪힐 수밖에 없습니다. Kafka는 이러한 문제를 해결하기 위해 설계된 분산형 메시지 시스템으로, 높은 처리량과 확장성을 제공합니다.
이 글에서는 대량의 데이터를 효율적으로 처리할 방법을 찾고 있는 분들을 위해 Kafka의 특징과 장점, 다양한 사용 사례, 그리고 사용 시 주의해야 할 사항에 대해 알려드리겠습니다.
카프카(Kafka)란?

‘Apache Kafka’는 고성능 분산 메시지 스트리밍 플랫폼으로, 대량의 데이터를 실시간으로 처리하고 전송하기 위해 설계되었습니다. LinkedIn에서 개발된 카프카(Kafka)는 2011년 오픈 소스로 공개되었으며, 현재 여러 기업과 개발자들 사이에서 널리 사용되고 있습니다.
카프카(Kafka)가 개발된 주된 이유는 데이터의 흐름을 효율적으로 관리하고, 다양한 시스템 간의 데이터 통신을 원활하게 하기 위함입니다. 특히 대규모 데이터 환경에서 발생할 수 있는 문제, 예를 들어 데이터 손실, 지연, 복잡한 데이터 파이프라인 구축 등을 해결하기 위해 고안되었습니다. Kafka는 이러한 요구에 부응하여 높은 처리량과 확장성을 제공합니다.
카프카(Kafka)는 어떻게 동작하는가
Kafka의 동작 원리를 이해하기 위해서는 먼저 ‘메시지 큐’와 ‘Pub/Sub 모델’이 무엇인지 알아야 합니다.
이 두 구조는 데이터 흐름의 효율성을 높이고, 시스템 간의 결합도를 낮춰 각 시스템이 독립적으로 작동할 수 있도록 돕습니다.
이를 통해 대규모 시스템에서 데이터 전송과 처리의 복잡성을 줄일 수 있습니다.
Kafka도 이러한 원리를 기반으로 작동하기 때문에, Kafka의 동작 원리를 이해하려면 메시지 큐와 Pub/Sub 모델을 제대로 파악하는 것이 중요합니다.
- 메시지 큐(Message Queue)

메시지 큐(Message Queue)는 분산 환경에서 시스템 간 데이터를 교환하기 위해 사용되는 통신 기법입니다. 데이터의 생산자와 소비자 간의 비동기적 통신을 가능하게 하는 시스템입니다.
메시지는 Queue에 저장되며, 소비자는 필요할 때 이 메시지를 읽어들여 처리할 수 있습니다. 카프카(Kafka)는 이러한 메시지 큐의 기능을 강화하여 수많은 소비자가 동시에 데이터를 소비할 수 있도록 설계되었습니다.
메시지 큐의 한계는 Producer(또는 Publisher)가 발행한 메시지를 보관하고 있는 큐에서 소비자가 메시지를 사용했을 때 다른 소비자는 해당 메시지를 재사용 할 수 없다는 것입니다. 이러한 한계를 해결하기 위해 카프카(Kafka)는 Pub/Sub 모델을 활용했습니다.
- Pub/Sub 모델

Pub/Sub 모델은 메시지 큐에서 제한된 메시지 재사용의 한계점을 해결해 주는 모델입니다. 데이터 생산자와 소비자 간의 느슨한 결합을 가능하게 해주며, 메시지를 1:1로 직접 송수신하지 않고 중앙 중계 시스템을 통해 전달됩니다.
발행자는 데이터(Message)를 특정 주제(Topic)에 발행하고, 주제(Topic)를 구독한 구독자에게 메시지를 전파해 줍니다. Pub/Sub 모델을 통해 구독자는 관심 있는 주제만 구독하여 필요한 데이터를 수신할 수 있습니다.
카프카(Kafka)는 이러한 Pub/Sub 모델을 활용하여 다양한 데이터 소스와 소비자가 상호작용할 수 있도록 합니다.
카프카(Kafka)의 동작 방식과 구조 설명

일반적인 메시지 큐는 Broker가 특정 큐에 저장한 메시지를 소비자가 읽는 구조입니다. 반면 Kafka는 분산 아키텍처를 기반으로 하며, 각 Broker가 메시지를 저장하고 클러스터가 전체 메시지를 처리합니다. 데이터는 Topic 별로 분류되고, 여러 파티션에 분산 저장된다는 점이 다릅니다.
일반적인 메시지 큐에서는 데이터를 처리할 때 큐에 쌓인 메시지를 1명의 소비자가 처리하게 되어있습니다. 하지만 분산 아키텍처(Partition)에서의 메시지 처리는 여러 명의 소비자가 병렬로 처리할 수 있기 때문에 트래픽을 유동적으로 빠르게 처리 할 수 있습니다.
소셜 미디어 플랫폼에서 사용자가 좋아요를 누르거나 댓글을 달면 활동 로그를 추적해 알고리즘을 업데이트한다고 가정해 보겠습니다. 이때 Kafka가 어떻게 동작하는지 살펴보겠습니다.
- Producer: 사용자가 게시물에 좋아요를 클릭하거나 댓글을 달면 해당 이벤트가 Producer에 의해 생성됩니다. 이 이벤트 정보는 “활동 로그”라는 Topic에 발행됩니다.
- Broker: Broker는 이 이벤트 정보를 받아 “활동 로그” Topic의 파티션에 저장합니다. 여러 사용자로부터 발생하는 이벤트가 동시에 처리되므로 높은 처리량을 유지합니다.
- Consumer: “활동 로그” Topic을 구독하는 실시간 분석 시스템이나 추천 알고리즘을 실행하는 Consumer가 사용자 활동 데이터를 실시간으로 읽고, 이를 기반으로 추천 콘텐츠를 업데이트하거나 통계를 생성합니다.
이러한 방식으로 Kafka는 실시간 스트리밍 애플리케이션에서 데이터 흐름을 효율적으로 관리합니다. 각 구성 요소가 독립적으로 동작하기 때문에 확장성도 매우 높습니다.
카프카(Kafka)를 사용해
대량의 데이터 처리 문제를 해결한 기업의 사례
# 링크드인(LinkedIn)

(이미지 출처: LinkedIn 홈페이지)
구 LinkedIn의 시스템 아키텍처는 ‘엔드 투 엔드 방식’으로, 서비스 간의 의존도가 높고 시스템이 복잡했습니다. 이러한 복잡성을 해결하기 위해 중앙에서 이벤트(데이터)를 송수신하고, 서비스들은 이를 가져다 쓰는 이벤트 기반 아키텍처(Event Driven Architecture)로 변경했습니다.

(카프카 사용 전 링크드인의 시스템 아키텍처 처리 방식)

(카프카 사용 전 링크드인의 시스템 아키텍처 처리 방식)
그림에서 볼 수 있듯이, Kafka 클러스터는 이벤트 송수신의 역할만 하게 되고, 각 서비스는 서로 어떤 기능을 하는지, 어떤 데이터를 쓸 건지 알 필요가 없게 됩니다. 이렇게 중앙 집중형 클러스터가 도입되면서 서비스 간의 의존도가 감소하고 시스템이 더욱 유연해졌습니다.
#잘난도(Zalando)

(이미지 출처: Zalando 홈페이지)
Zalando라는 해외 온라인 쇼핑몰의 Kafka 도입 사례를 살펴보겠습니다. Zalando는 2015년 이후 꾸준한 노력을 통해, 2020년 기준으로 월간 활성 사용자 3,100만 명, 연간 주문 1억 5천만 건이라는 엄청난 성장을 이루었습니다. 하지만 급격히 증가하는 트래픽에 기존의 API 기반 데이터베이스 CRUD 방식으로는 데이터 처리에 한계를 느끼기 시작했습니다.
Zalando는 기존의 동기 처리 방식에서 이벤트 기반(Event-Driven) 아키텍처로 전환하면서, 카프카(Kafka)라는 대표적인 비동기 스트리밍 플랫폼을 도입하게 됩니다.
카프카(Kafka)는 데이터의 순서를 보장하고, 대량의 데이터를 비동기 방식으로 빠르고 병렬적으로 처리할 수 있어, 기존 CRUD 방식에서 트래픽이 많아질 경우 과부하가 걸리던 문제를 해결할 수 있었습니다.
* CRUD 방식의 데이터 처리 방법이 궁금한 분들은 아래 링크를 통해 확인할 수 있습니다.
▶️ Prisma란? 기업들의 사용 사례부터 적용 방법까지의 사용 종합 가이드
Kafka 사용법
간단한 설정으로 Kafka의 메시지가 어떻게 송, 수신 되는지 알아보겠습니다. 이를 위해 먼저 docker-compose를 이용해 Kafka 서버를 구동하겠습니다. 아래 서비스들은 하나의 Kafka 클러스터가 됩니다.
version: '3.8' services: zookeeper-1: image: confluentinc/cp-zookeeper:latest ports: - '32181:32181' environment: ZOOKEEPER_CLIENT_PORT: 32181 ZOOKEEPER_TICK_TIME: 2000 kafka-1: image: confluentinc/cp-kafka:latest ports: - '9092:9092' depends_on: - zookeeper-1 environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-1:29092,EXTERNAL://localhost:9092 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_NUM_PARTITIONS: 3 kafka-2: image: confluentinc/cp-kafka:latest ports: - '9093:9093' depends_on: - zookeeper-1 environment: KAFKA_BROKER_ID: 2 KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-2:29093,EXTERNAL://localhost:9093 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_NUM_PARTITIONS: 3 kafka-3: image: confluentinc/cp-kafka:latest ports: - '9094:9094' depends_on: - zookeeper-1 environment: KAFKA_BROKER_ID: 3 KAFKA_ZOOKEEPER_CONNECT: zookeeper-1:32181 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-3:29094,EXTERNAL://localhost:9094 KAFKA_DEFAULT_REPLICATION_FACTOR: 3 KAFKA_NUM_PARTITIONS: 3 kafka-ui: image: provectuslabs/kafka-ui container_name: kafka-ui ports: - "8989:8080" restart: always environment: - KAFKA_CLUSTERS_0_NAME=local - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-1:29092,kafka-2:29093,kafka-3:29094 - KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper-1:22181 |
위 도커파일 작성 후 docker-compose up -d –build 명령어로 구동합니다. 이후 localhost:8989 에 접속하면 아래와 같은 화면이 나타납니다. Kafka의 구성 요소인 Topic, Producer, Consumer, Broker 의 정보를 ui로 볼 수 있습니다.

Java 코드로 Kafka를 연동하는 방법
Kakfa서버를 실무에서 어떻게 연동하고 이용하는지도 함께 살펴보겠습니다. 먼저 필요한 라이브러리를 주입 받습니다.
implementation 'org.springframework.kafka:spring-kafka' |
Producer 설정을 빈으로 등록해줍니다. 메시지를 발행하는 Producer의 설정입니다. 메시지의 크기, 컨버팅 방식 등을 설정 할 수 있습니다.
@Configuration public class KafkaProducerConfig { private static final String END_POINT = "127.0.0.1:9092"; // Kafka host @Bean public ProducerFactory<String, Object> producerFactory() { Map<String, Object> configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, END_POINT); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); return new DefaultKafkaProducerFactory<>(configProps); } @Bean public KafkaTemplate<String, Object> kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } } |
그 다음 Consumer 설정입니다. Producer가 발행한 메시지를 수신받는 소비자의 설정이며, 메시지의 수신 주기, 사이즈 등을 설정 할 수 있습니다.
@EnableKafka @Configuration public class KafkaConsumerConfig { private static final String END_POINT = "127.0.0.1:9092"; // Kafka host // default @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(getDefaultConfigProps())); return factory; } private Map<String, Object> getDefaultConfigProps() { Map<String, Object> defaultConfigProps = new HashMap<>(); defaultConfigProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, END_POINT); defaultConfigProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); defaultConfigProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return defaultConfigProps; } } |
메시지를 수신 받을 listener 설정입니다. 위 Consumer 설정으로 등록한 Container를 기반으로 메시지를 수신 받습니다.
@Component public class KafkaListener { @KafkaListener(topics = "test-topic", groupId = "test-topic-group-01") public void sendTest(ConsumerRecord<String, Object> record) { System.out.println("send message : " + record.value()); } } |
메시지를 송수신 하는 과정입니다. 메시지가 저장 될 토픽을 생성 후 Hello world 메시지를 보내주세요.



Kafka 사용 시 주의사항
Kafka 사용 시 알아야할 3가지
- Kafka의 병렬 처리
Kafka의 큰 장점 중 하나는 병렬 처리 능력입니다. Kafka의 파티션 구조 덕분에, 하나의 Consumer 그룹 내에서 여러 Consumer들이 데이터를 병렬로 빠르게 처리할 수 있습니다. 이 설명을 들으면 “Consumer를 많이 늘리면 더 빨리 처리되지 않을까?“라고 생각할 수 있습니다. 그러나 Consumer를 무작정 늘리기만 해서는 효과적이지 않습니다.
Kafka에서는 하나의 파티션을 오직 하나의 Consumer만 읽을 수 있기 때문에, 파티션보다 많은 Consumer를 추가하면 나머지 Consumer들은 대기 상태에 들어갑니다. 이는 불필요한 자원 낭비로 이어집니다.
또한 파티션과 Consumer를 비례해 무한정 늘리면 파일 핸들러와 같은 리소스가 과도하게 사용되어 시스템에 부하가 발생할 수 있습니다. 따라서 애플리케이션에 적합한 파티션과 Consumer 수를 적절히 설정하는 것이 중요합니다.
- 예외 처리와 메시지 유실 문제
Kafka는 메시지를 처리하는 중에 예외가 발생할 경우 ‘자동 재처리 기능’을 제공합니다. 이는 Consumer가 메시지를 처리하는 도중 장애가 발생하면, 해당 메시지를 다시 수신할 수 있도록 대기열에 넣어주는 기능입니다.
그러나 모든 예외를 예측할 수 없는 것처럼, Kafka도 완벽하지는 않습니다. 예를 들어, Kafka에서 offset(Consumer가 메시지를 어디까지 읽었는지를 기록하는 값)을 업데이트하는 옵션을 설정하면, 예외가 발생했음에도 메시지가 정상 처리된 것으로 간주되어 메시지가 유실될 수 있습니다.
따라서 이러한 상황을 방지하기 위해서는 별도의 예외 처리 핸들링을 구현해 유실되는 메시지가 없도록 주의해야 합니다.
- 적절한 도구 선택의 중요성
모든 기술은 애플리케이션의 데이터 종류, 양, 현재 아키텍처 등을 고려해 적절하게 사용해야 합니다. Kafka도 결국 하나의 메시지 큐일 뿐입니다. 메시지 큐의 목적은 메시지를 발행하고, 그 메시지를 필요한 주체가 가져다 소비하는 것입니다.
잘못하면 오버 엔지니어링이 될 수 있기 때문에, 무조건 Kafka를 도입하기보다는 애플리케이션에 맞는 기술을 선택하는 것이 중요합니다.
Kafka는 대규모 메시지를 저장하고 분산하는 데 적합하며, 대용량 데이터 처리가 아닌 경우에는 RabbitMQ와 같은 다른 메시지 큐가 더 적합할 수 있습니다.
Kafka를 사용하면서 어떤 옵션을 설정할지, 몇 개의 파티션과 Consumer 그룹으로 나눌지, 메시지 수신과 offset 처리는 어떻게 할지를 결정하는 것이 성능에 큰 영향을 미칩니다. 따라서 Kafka에 대한 전반적인 이해와 실무 경험이 있는 전문가를 두는 것이 매우 중요합니다.
'컴퓨터 활용(한글, 오피스 등) > 기타' 카테고리의 다른 글
| 웹 스토리지(로컬 스토리지, 세션스토리지)의 모든 것 with 세션, 쿠키 (0) | 2025.11.04 |
|---|---|
| [Web] 서비스의 큰 그림, 아키텍처 (0) | 2025.11.03 |
| Free WYSIWYG Web Editors for Windows (0) | 2025.10.31 |
| 웹기반 WYSIWYG HTML에디터 오픈소스 (0) | 2025.10.29 |
| 개요 보기(한글과 MS 워드 비교) (0) | 2025.10.29 |