Event-Driven Architecture 101: Kafka로 간단한 앱 만들기 - Gopi Gugan

발행: (2026년 1월 16일 오전 01:02 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

번역할 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.

카프카란 무엇인가 (쉽게 설명)?

Apache Kafka는 분산 이벤트 스트리밍 플랫폼으로 다음을 위해 사용됩니다:

  • 이벤트 게시 (프로듀서)
  • 이벤트를 영구적으로 저장 (토픽)
  • 이벤트 소비 (컨슈머)

서비스가 서로 직접 호출하는 대신, 이벤트를 Kafka에 발행합니다. 다른 서비스는 준비가 되었을 때 그 이벤트에 비동기적으로 반응합니다.

Kafka를 매우 신뢰할 수 있고 확장 가능한 이벤트 로그라고 생각하면 됩니다.

언제 Kafka를 사용해야 할까요?

Kafka는 다음과 같은 경우에 강력한 선택입니다:

  • 서비스 간 비동기 통신
  • 고처리량 데이터 파이프라인
  • 실시간 처리
  • 마이크로서비스의 결합도 낮춤

다음 경우라면 Kafka가 필요하지 않을 가능성이 높습니다:

  • 서비스가 하나뿐인 경우
  • 단순 요청/응답 API에 의존하는 경우
  • 규모가 작고 예측 가능한 경우

Kafka는 강력하지만, 불필요한 복잡성은 여전히 복잡성입니다.

고수준 아키텍처

  1. 프로듀서는 이벤트를 토픽에 전송합니다.
  2. 카프카는 이벤트를 영구적으로 저장합니다.
  3. 하나 이상의 컨슈머가 자신의 속도에 맞춰 이벤트를 읽습니다.

이는 서비스 간의 강한 결합을 없애고 연쇄적인 장애 발생을 방지합니다.

단계 1: Docker로 로컬에서 Kafka 실행

가장 빠르게 시작하는 방법은 Docker입니다.

# docker-compose.yml
version: "3"
services:
  zookeeper:
    image: confluentinc/cp-zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

Kafka 시작:

docker compose up -d

이제 로컬에서 작동하는 Kafka 브로커가 실행되고 있습니다.

단계 2: 프로듀서 만들기 (Node.js)

프로듀서의 유일한 책임은 이벤트를 발생시키는 것입니다.

import { Kafka } from "kafkajs";

const kafka = new Kafka({
  brokers: ["localhost:9092"],
});

const producer = kafka.producer();

async function sendEvent() {
  await producer.connect();

  await producer.send({
    topic: "orders",
    messages: [
      {
        value: JSON.stringify({
          orderId: 123,
          total: 49.99,
        }),
      },
    ],
  });

  await producer.disconnect();
}

sendEvent();

핵심 요점: 프로듀서는 이벤트를 누가 소비하는지 알지 못합니다.

Step 3: 소비자 생성

소비자는 이벤트에 독립적으로 반응합니다.

import { Kafka } from "kafkajs";

const kafka = new Kafka({
  brokers: ["localhost:9092"],
});

const consumer = kafka.consumer({ groupId: "billing-service" });

async function run() {
  await consumer.connect();
  await consumer.subscribe({ topic: "orders" });

  await consumer.run({
    eachMessage: async ({ message }) => {
      const event = JSON.parse(message.value.toString());
      console.log("Processing order:", event.orderId);
    },
  });
}

run();

이제 추가 서비스(배송, 분석, 알림)를 프로듀서를 변경하지 않고 추가할 수 있습니다.

왜 이벤트‑드리븐 아키텍처는 확장성이 좋은가

전통적인 아키텍처이벤트‑드리븐 아키텍처
긴밀한 결합느슨한 결합
동기 호출비동기 이벤트
확장 어려움수평 확장 가능
취약한 실패복원력 있는 시스템

Kafka는 서비스 간 충격 흡수기 역할을 합니다.

피해야 할 일반적인 Kafka 실수

  • Kafka를 큐처럼 다루기 (실제로는 로그입니다)
  • 너무 많은 작은 토픽 생성
  • 스키마 진화를 무시하기
  • 데이터베이스와 크론 작업이 더 간단할 때 Kafka를 사용하는 것

Kafka는 복잡성을 줄여야 하며, 오히려 늘려서는 안 됩니다.

Kafka가 슈퍼파워가 될 때

Kafka는 다음과 결합될 때 진가를 발휘합니다:

  • Schema Registry (Avro 또는 Protobuf)
  • Stream processing (Kafka Streams 또는 Flink)
  • 실시간 분석 파이프라인
  • 이벤트 기반 알림

이때, Kafka는 시스템의 중추 신경계가 됩니다.

Kafka는 두렵지 않습니다 — 단지 규칙이 있는 내구성 있는 이벤트 로그일 뿐입니다. 다음을 이해한다면:

  • Topics
  • Producers
  • Consumers
  • Consumer groups

이미 Kafka의 대부분을 이해한 것입니다.

Back to Blog

관련 글

더 보기 »

이벤트 기반 아키텍처 설명: 심층 탐구

Event-Driven Architecture Explained: A Deep Dive 오늘날 급변하는 소프트웨어 환경에서, 확장 가능하고 복원력 있으며 반응성이 뛰어난 애플리케이션을 구축하는 것은 p...