Event-Driven Architecture 101: Kafka로 간단한 앱 만들기 - Gopi Gugan
Source: Dev.to
번역할 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.
카프카란 무엇인가 (쉽게 설명)?
Apache Kafka는 분산 이벤트 스트리밍 플랫폼으로 다음을 위해 사용됩니다:
- 이벤트 게시 (프로듀서)
- 이벤트를 영구적으로 저장 (토픽)
- 이벤트 소비 (컨슈머)
서비스가 서로 직접 호출하는 대신, 이벤트를 Kafka에 발행합니다. 다른 서비스는 준비가 되었을 때 그 이벤트에 비동기적으로 반응합니다.
Kafka를 매우 신뢰할 수 있고 확장 가능한 이벤트 로그라고 생각하면 됩니다.
언제 Kafka를 사용해야 할까요?
Kafka는 다음과 같은 경우에 강력한 선택입니다:
- 서비스 간 비동기 통신
- 고처리량 데이터 파이프라인
- 실시간 처리
- 마이크로서비스의 결합도 낮춤
다음 경우라면 Kafka가 필요하지 않을 가능성이 높습니다:
- 서비스가 하나뿐인 경우
- 단순 요청/응답 API에 의존하는 경우
- 규모가 작고 예측 가능한 경우
Kafka는 강력하지만, 불필요한 복잡성은 여전히 복잡성입니다.
고수준 아키텍처
- 프로듀서는 이벤트를 토픽에 전송합니다.
- 카프카는 이벤트를 영구적으로 저장합니다.
- 하나 이상의 컨슈머가 자신의 속도에 맞춰 이벤트를 읽습니다.
이는 서비스 간의 강한 결합을 없애고 연쇄적인 장애 발생을 방지합니다.
단계 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의 대부분을 이해한 것입니다.