Blog
kafkastreamingllmrealtimeaidata-pipeline

Kafka + AI 실시간 파이프라인 - 스트리밍 데이터에 LLM 적용하기

Apache Kafka 스트리밍 데이터에 LLM 추론을 적용하는 실시간 AI 파이프라인 구축 방법을 정리합니다. 실시간 분류, 감성 분석, 이상 탐지, 자동 요약 파이프라인을 다룹니다.

Data Dynamics2026年4月16日11 min read
This post is not yet translated. The original Korean version is shown below.

고객 리뷰가 쏟아지는 쇼핑몰을 상상해보세요. 하루에 수만 건의 리뷰가 올라오는데, "지금 이 순간 불만이 폭발하고 있다"는 사실을 다음 날 배치 분석 리포트로 알면 이미 늦습니다. 실시간으로 감지해서 즉시 대응해야 하죠.

Kafka + LLM 파이프라인이 바로 그 역할입니다. 데이터가 생성되는 그 순간, LLM이 분류하고 분석해서 알림을 쏘는 실시간 AI 파이프라인을 이 글에서 단계별로 구축해봅니다.

이 글에서 배우는 것

  • 배치 처리와 실시간 처리의 차이, 그리고 실시간이 필요한 상황
  • Kafka Consumer + LLM 추론 서비스를 연결하는 아키텍처 설계
  • 감성 분류·이상 탐지 파이프라인 Python 구현
  • 마이크로 배치로 처리량을 5~10배 높이는 최적화 기법
  • 파티션 분산으로 처리량을 수평 확장하는 방법

1. 실시간 AI 파이프라인의 필요성

배치 vs 실시간

"배치로 하루에 한 번 돌리면 안 되나요?" — 물론 됩니다. 하지만 실시간 대응이 필요한 상황에서는 수분~수시간의 지연이 치명적입니다. 어떤 상황에서 실시간이 필요한지 먼저 파악하세요.

구분배치 처리 (Spark + LLM)실시간 처리 (Kafka + LLM)
지연시간수분~수시간수초~수십초
처리 방식일괄 수집 후 처리이벤트 발생 즉시 처리
적합 사례일 단위 보고서, 배치 분류실시간 알림, 즉시 대응
LLM 호출대량 배치 요청건별 또는 마이크로 배치

활용 시나리오

시나리오Kafka 토픽LLM 처리출력
실시간 감성 분석customer_reviews감성 분류대시보드 + 알림
실시간 이상 탐지server_logs로그 분석PagerDuty 알림
실시간 콘텐츠 분류user_posts카테고리 분류, 유해성 검사콘텐츠 필터링
실시간 번역chat_messages다국어 번역번역된 메시지 전달
실시간 요약news_articles뉴스 요약알림 서비스

2. 아키텍처

전체 구조

전체 흐름을 머릿속에 그려봅시다. 웹 이벤트·로그·IoT 데이터가 Kafka 입력 토픽으로 모이고, Consumer가 꺼내서 LLM에 넘기고, 결과를 출력 토픽으로 내보내는 구조입니다.

한 문장으로: Kafka는 고속 데이터 버퍼, LLM은 실시간 두뇌, Consumer는 이 둘을 잇는 연결고리입니다.

Loading diagram…

처리 패턴

Loading diagram…

3. 구현

Python Kafka Consumer + LLM

이제 직접 코드로 보겠습니다. 고객 리뷰를 실시간으로 읽어 감성 분류하고, 긴급 리뷰는 별도 알림 토픽으로 보내는 파이프라인입니다.

from confluent_kafka import Consumer, Producer
import json
import requests
 
# Kafka Consumer 설정
consumer = Consumer({
    'bootstrap.servers': 'kafka:9092',
    'group.id': 'llm-processor',
    'auto.offset.reset': 'latest',
    'enable.auto.commit': False,
    'max.poll.interval.ms': 300000,
})
 
producer = Producer({'bootstrap.servers': 'kafka:9092'})
 
consumer.subscribe(['customer_reviews'])
 
def classify_with_llm(text: str) -> dict:
    """LLM으로 리뷰 분류"""
    response = requests.post(
        "http://vllm-server:8000/v1/chat/completions",
        json={
            "model": "meta-llama/Llama-3.1-8B-Instruct",
            "messages": [{"role": "user", "content": f'리뷰를 분석하세요. JSON 반환: {{"sentiment": "긍정/부정/중립", "category": "카테고리", "urgent": true/false}}\n\n리뷰: {text}'}],
            "temperature": 0.0,
            "max_tokens": 100
        },
        timeout=10
    )
    return json.loads(response.json()["choices"][0]["message"]["content"])
 
# 메시지 처리 루프
while True:
    msg = consumer.poll(1.0)
    if msg is None:
        continue
    if msg.error():
        continue
    
    review = json.loads(msg.value().decode('utf-8'))
    
    try:
        result = classify_with_llm(review['text'])
        
        # 결과를 출력 토픽에 전송
        output = {**review, **result, "processed_at": datetime.now().isoformat()}
        producer.produce(
            'review_analysis',
            key=msg.key(),
            value=json.dumps(output).encode('utf-8')
        )
        
        # 긴급 리뷰는 알림
        if result.get("urgent"):
            producer.produce('urgent_alerts', value=json.dumps(output).encode('utf-8'))
        
        consumer.commit(msg)
        
    except Exception as e:
        # 실패 시 DLQ(Dead Letter Queue)로 전송
        producer.produce('review_dlq', value=msg.value())
        consumer.commit(msg)
 
    producer.flush()

마이크로 배치 처리

건별로 LLM을 호출하면 처리량이 너무 낮습니다. 5초 또는 10건 중 먼저 도달하는 조건으로 메시지를 모아서 한 번에 보내면 처리량이 5~10배 높아집니다.

import asyncio
import aiohttp
from collections import deque
 
class MicroBatchProcessor:
    def __init__(self, batch_size=10, batch_timeout=5.0):
        self.batch_size = batch_size
        self.batch_timeout = batch_timeout
        self.buffer = deque()
    
    async def process_stream(self, consumer, producer):
        """마이크로 배치 기반 스트림 처리"""
        while True:
            msg = consumer.poll(0.1)
            if msg and not msg.error():
                self.buffer.append(msg)
            
            # 배치 크기 또는 타임아웃 도달 시 처리
            if len(self.buffer) >= self.batch_size or \
               (self.buffer and self._timeout_reached()):
                batch = [self.buffer.popleft() for _ in range(min(len(self.buffer), self.batch_size))]
                
                # 배치 LLM 호출
                results = await self.batch_classify(batch)
                
                for msg, result in zip(batch, results):
                    producer.produce('output_topic', value=json.dumps(result).encode())
                    consumer.commit(msg)
                
                producer.flush()
    
    async def batch_classify(self, messages):
        """배치 LLM 호출"""
        texts = [json.loads(m.value())['text'] for m in messages]
        # vLLM 배치 API 호출
        async with aiohttp.ClientSession() as session:
            tasks = [self.call_llm(session, t) for t in texts]
            return await asyncio.gather(*tasks)

4. 실시간 이상 탐지 파이프라인

서버 로그를 실시간으로 분석해 이상 징후를 즉시 잡아내는 파이프라인입니다. 규칙 기반 알람은 미리 정의한 패턴만 잡지만, LLM은 맥락을 이해해 새로운 유형의 이상도 찾아냅니다.

# 서버 로그 실시간 분석
def analyze_log(log_entry: str) -> dict:
    """로그 엔트리를 LLM으로 분석"""
    response = requests.post(
        "http://ollama:11434/api/generate",
        json={
            "model": "llama3.1:8b",
            "prompt": f"""다음 서버 로그를 분석하세요.
 
로그: {log_entry}
 
JSON 반환:
{{"severity": "critical/warning/info", "category": "memory/disk/network/application/security", "requires_action": true/false, "summary": "한줄 요약"}}""",
            "stream": False,
            "options": {"temperature": 0.0}
        }
    )
    return json.loads(response.json()["response"])

5. 성능 최적화

실시간 파이프라인의 가장 큰 병목은 LLM 추론 속도입니다. 다음 전략들을 조합하면 같은 하드웨어에서 처리량을 크게 높일 수 있습니다.

전략설명지연시간처리량
마이크로 배치N건 모아서 1회 호출+수초5~10x
비동기 호출asyncio로 병렬 LLM 호출동일3~5x
경량 모델7B Q4 모델 사용-50%2x
결과 캐싱유사 입력 캐시-90% (캐시 히트)-
컨슈머 스케일링파티션 수만큼 컨슈머 확장동일N배
우선순위 큐긴급/일반 분리 처리긴급: -80%-

파티션과 컨슈머 스케일링

처리량이 부족하면 단순히 Consumer와 LLM 서버를 추가하면 됩니다. Kafka 파티션 수만큼 Consumer를 늘리면 처리량이 선형으로 확장됩니다.

Loading diagram…

참고: 실시간 LLM 파이프라인에서 가장 큰 병목은 LLM 추론 속도입니다. 경량 모델(7B Q4)과 마이크로 배치를 조합하면 대부분의 실시간 요구사항을 충족할 수 있습니다.


마치며 — 핵심 요약

  • 실시간 AI 파이프라인은 Kafka(버퍼) + Consumer(연결) + LLM(분석) + 출력 토픽(전달) 네 가지 조각으로 구성됩니다.
  • 마이크로 배치(5초 또는 10건)로 LLM 호출을 묶으면 처리량이 5~10배 오르고 API 비용도 절감됩니다.
  • **경량 모델(7B Q4)**을 로컬에서 vLLM으로 서빙하면 지연시간이 절반으로 줄어들고 외부 의존도가 사라집니다.
  • 처리 실패 시 **DLQ(Dead Letter Queue)**로 보내서 재처리 경로를 확보하는 것이 운영 안정성의 핵심입니다.
  • 파티션 수만큼 Consumer를 늘리면 처리량이 선형으로 확장됩니다. 먼저 충분한 파티션 수(최소 12개 권장)로 토픽을 설계하세요.
  • 처음엔 건별 처리 → 마이크로 배치 → 비동기 병렬 순서로 단계적으로 최적화해나가면 됩니다.

References


— Data Dynamics 엔지니어링 팀