Debezium 실무 스터디 가이드

데이터 엔지니어 · AI 엔지니어를 위한 핵심 요약

CDC Kafka Database Change Data Capture 실시간 데이터 마이크로서비스

작성일: 2026.03

1. Debezium란?

Debezium는 오픈소스 Change Data Capture (CDC) 플랫폼입니다. 데이터베이스의 트랜잭션 로그(예: MySQL의 binlog, PostgreSQL의 WAL)를 모니터링하여 데이터 변경 사항을 실시간으로 캡처하고, 이를 이벤트 스트림(주로 Apache Kafka)으로 전송하는 역할을 합니다.

마이크로서비스 아키텍처, 데이터 레이크/웨어하우스 적재, 캐시/검색엔진 동기화 등 실시간 데이터 파이프라인의 핵심 컴포넌트로 사용됩니다.

🔍 로그 기반 CDC

데이터베이스의 트랜잭션 로그를 직접 읽어 변경을 감지합니다. 트리거나 쿼리 폴링 방식에 비해 원본 DB 부하가 극히 적고, 모든 변경 사항을 정확히 포착할 수 있습니다.

⚡ 실시간 스트리밍

변경 이벤트를 거의 실시간으로 Kafka 토픽에 발행합니다. 이벤트는 JSON 또는 Avro 형식으로, 스키마 레지스트리와 연동 가능합니다.

🔗 커넥터 생태계

MySQL, PostgreSQL, MongoDB, SQL Server, Oracle, Cassandra 등 주요 DBMS용 커넥터를 제공합니다. Kafka Connect 프레임워크 위에서 동작해 확장성이 뛰어납니다.

2. 왜 써야 하는가?

기존 데이터 동기화 방식과 Debezium의 로그 기반 CDC를 비교합니다.

방식 원리 장점 단점
애플리케이션 이중 작성 비즈니스 로직에서 두 시스템에 각각 INSERT/UPDATE 간단한 구현 데이터 불일치 위험高, 코드 복잡도 증가, 트랜잭션 보장 어려움
폴링 (Polling) updated_at 컬럼을 주기적으로 SELECT DB 종속성 낮음 실시간성 떨어짐, 변경 누락 가능성, 원본 DB 부하 증가
트리거 (Trigger) DB 트리거로 변경 시 별도 테이블에 기록 모든 변경 캡처 가능 원본 DB 성능 저하, 유지보수 복잡, 확장성 낮음
Debezium (로그 기반 CDC) 트랜잭션 로그(binlog/WAL) 읽기 원본 DB 부하 극소, 실시간성, 정확한 변경 이력, 확장성 高 DB 로그 포맷 이해 필요, 설정 복잡도, 추가 인프라(Kafka) 필요

💡 선택 가이드

Debezium를 선택해야 할 때: 마이크로서비스 간 데이터 동기화, 실시간 분석/대시보드, 데이터 레이크 적재, 캐시/검색엔진 동기화, 데이터 마이그레이션 모니터링 등 실시간성과 정확성이 모두 중요한 시나리오입니다.

3. 핵심 개념

Debezium는 Kafka Connect의 소스 커넥터(Source Connector)로 동작합니다. 아래는 전형적인 아키텍처입니다.

+----------------+ +-------------+ +-------------------+ +-------------------+ | | | | | | | | | Source DB |----->| DB Log |----->| Debezium |----->| Apache Kafka | | (MySQL, PG) | | (binlog/WAL)| | Connector | | | | | | | | | | | +----------------+ +-------------+ +-------------------+ +-------------------+ | v +-------------------+ | | | Consumer Apps | | (Stream Proc, | | Data Lake, ES) | | | +-------------------+

주요 컴포넌트

📦 이벤트 구조

Kafka로 전송되는 각 메시지(이벤트)는 key와 value를 가집니다. key는 기본 키를, value는 변경 전/후 데이터와 메타데이터를 포함합니다.

4. 퀵스타트 (MySQL + Docker)

Docker Compose를 사용해 MySQL, Zookeeper, Kafka, Kafka Connect (Debezium)를 한 번에 실행합니다.

단계 1: docker-compose.yml 작성

version: '3.8'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: debezium
      MYSQL_USER: debezium
      MYSQL_PASSWORD: dbz
    ports:
      - "3306:3306"
    command: --binlog-format=ROW --default-authentication-plugin=mysql_native_password

  connect:
    image: debezium/connect:latest
    depends_on:
      - kafka
      - mysql
    environment:
      BOOTSTRAP_SERVERS: kafka:9092
      GROUP_ID: 1
      CONFIG_STORAGE_TOPIC: connect_configs
      OFFSET_STORAGE_TOPIC: connect_offsets
      STATUS_STORAGE_TOPIC: connect_statuses
    ports:
      - "8083:8083"

단계 2: 인프라 실행 및 커넥터 등록

아래 명령어를 터미널에서 실행합니다.

# 1. 인프라 실행
docker-compose up -d

# 2. MySQL에 샘플 데이터베이스 및 테이블 생성 (다른 터미널에서)
docker exec -it <mysql-container-id> mysql -u root -pdebezium

MySQL 클라이언트에서:

CREATE DATABASE inventory;
USE inventory;

CREATE TABLE customers (
  id INT PRIMARY KEY AUTO_INCREMENT,
  first_name VARCHAR(50),
  last_name VARCHAR(50),
  email VARCHAR(100)
);

INSERT INTO customers (first_name, last_name, email) VALUES
('John', 'Doe', 'john@example.com'),
('Jane', 'Smith', 'jane@example.com');

단계 3: Debezium MySQL 커넥터 등록

Kafka Connect REST API를 호출해 커넥터를 생성합니다.

curl -X POST http://localhost:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "inventory-connector",
    "config": {
      "connector.class": "io.debezium.connector.mysql.MySqlConnector",
      "tasks.max": "1",
      "database.hostname": "mysql",
      "database.port": "3306",
      "database.user": "root",
      "database.password": "debezium",
      "database.server.id": "184054",
      "database.server.name": "dbserver1",
      "database.include.list": "inventory",
      "table.include.list": "inventory.customers",
      "database.history.kafka.bootstrap.servers": "kafka:9092",
      "database.history.kafka.topic": "dbhistory.inventory",
      "include.schema.changes": "true"
    }
  }'

단계 4: 이벤트 소비 (Python 예제)

from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    'dbserver1.inventory.customers',  # 토픽명: {server}.{database}.{table}
    bootstrap_servers='localhost:9092',
    auto_offset_reset='earliest',
    value_deserializer=lambda m: json.loads(m.decode('utf-8'))
)

print("Listening for changes...")
for message in consumer:
    event = message.value
    op = event['op']  # 'c'=create, 'u'=update, 'd'=delete
    source = event['source']
    after = event.get('after', {})
    
    print(f"Operation: {op} | Table: {source['table']} | Data: {after}")

⚠️ 주의사항

프로덕션 환경에서는 database.password를 안전하게 관리하고, SSL 설정, 적절한 tasks.max 및 토픽 복제 계수를 설정해야 합니다.

5. 핵심 설정 레퍼런스

MySQL 커넥터를 기준으로 한 필수 및 주요 설정 목록입니다.

설정 기본값 설명
connector.class - 반드시 지정. io.debezium.connector.mysql.MySqlConnector
database.hostname - DB 서버 호스트명
database.user, database.password - DB 접속 계정 (로그 읽기 권한 필요)
database.server.name - 논리적 서버명. Kafka 토픽명의 접두사로 사용됨
database.include.list 모든 DB 모니터링할 DB 화이트리스트 (쉼표 구분)
table.include.list 모든 테이블 모니터링할 테이블 화이트리스트 (예: inventory.customers)
include.schema.changes true DDL 변경(테이블 생성/수정) 이벤트도 캡처할지 여부
snapshot.mode initial 초기 스냅샷 방식: initial(전체), when_needed, never
decimal.handling.mode precise DECIMAL 타입 처리: precise(문자열), double, string
time.precision.mode adaptive 시간 타입 정밀도: adaptive(마이크로초), connect(밀리초)
tombstones.on.delete true 삭제 이벤트 시 null 값의 토큰스톤 메시지 발행 여부 (로그 압축용)

6. 실무 팁 & 트러블슈팅

자주 겪는 문제

🚫 커넥터 시작 실패

증상: 커넥터 상태가 FAILED, 로그에 "binlog 접근 불가" 오류.

해결:

  • MySQL의 binlog_format=ROW 확인.
  • DB 사용자에게 REPLICATION SLAVE, REPLICATION CLIENT 권한 부여.
  • 호스트명/포트 방화벽 확인.

🐌 이벤트 지연 (Lag)

증상: Kafka 메시지 생산은 되지만 실시간성이 떨어짐.

해결:

  • tasks.max 증가 (테이블이 많을 경우).
  • Kafka 브로커 및 네트워크 성능 점검.
  • 커넥터 배치 사이즈 조정 (max.batch.size).

📈 스키마 변경 대응

증상: ALTER TABLE 후 컨슈머에서 필드 누락 또는 파싱 오류.

해결:

  • include.schema.changes=true로 DDL 이벤트 수신.
  • Avro + Schema Registry 사용 시 호환성 모드 설정.
  • 컨슈머가 스키마 변경에 유연한 라이브러리 사용 (예: Apache Spark).

성능 튜닝 팁

🔧 유용한 진단 명령어

# 커넥터 상태 확인
curl -s http://localhost:8083/connectors/inventory-connector/status | jq .

# 커넥터 설정 조회
curl -s http://localhost:8083/connectors/inventory-connector | jq .

# Kafka 토픽 메시지 확인 (최근 5개)
docker exec <kafka-container> kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic dbserver1.inventory.customers \
  --from-beginning --max-messages 5

7. 참고 자료