펜타시스템/입사 후 첫 프로젝트 공부 내용

CUBRID -> Postgres 마이그레이션 후 Debezium 개요

dev-lee 2026. 9. 29. 16:31

Debezium 속성 이름은 버전(1.x, 2.x, 3.x)마다 조금씩 다름. 실제 설정 시 설치한 버전의 문서로 확인해야 한다. CUBRID는 Debezium 공식 커넥터 목록에 없는 것으로 알고 있음. 이 글의 내용은 모두 PostgreSQL 기준이다.


1. Debezium 개요와 트리거 테이블 방식 비교

트랜잭션 로그 기반 CDC 도구 Debezium의 동작 방식, 트리거 테이블 방식과의 차이, 성능, 기존 NiFi 흐름 전환 시 영향 정리

1.1 Debezium

DB의 변경 내용(INSERT/UPDATE/DELETE)을 실시간으로 잡아서 Kafka로 보내주는 오픈소스 CDC 도구이다.

  • 트랜잭션 로그 읽기 — DB는 모든 변경을 트랜잭션 로그에 먼저 기록함. Debezium은 이 로그를 직접 읽어서 변경 이벤트로 만듦
  • Kafka Connect 커넥터 — Kafka Connect의 커넥터로 실행됨. 테이블마다 Kafka 토픽이 하나씩 생기고 변경 이벤트가 쌓임
  • 스냅샷 후 변경분 추적 — 처음 연결할 때 기존 데이터를 한 번 전부 읽고(스냅샷), 그 다음부터는 변경분만 따라감
DB  Debezium이 읽는 로그
MySQL binlog
PostgreSQL WAL (logical decoding)
Oracle LogMiner
SQL Server, MongoDB, Db2 등 지원

이벤트 형태를 간단히 보면 다음과 같다.

{
  "op": "u",
  "before": { "rcpt_no": "...", "msg_incl_url": "이전 값" },
  "after":  { "rcpt_no": "...", "msg_incl_url": "바뀐 값" },
  "source": { "table": "...", "ts_ms": 1759132800000 }
}
op 값 의미
c 생성
u 수정
d 삭제
r 스냅샷 읽기

바뀌기 전(before)과 바뀐 후(after)의 값이 한 이벤트에 같이 옴

1.2 트리거 테이블 방식과 비교

목적은 같다. "어떤 행이 바뀌었는지 기록해 두고, 뒤에서 가져가 처리한다"는 것이다.

항목  트리거 테이블 Debezium
원본 DB 부담 INSERT마다 트리거가 트리거 테이블에 한 번 더 INSERT 로그만 읽어서 쓰기 작업에 영향이 거의 없음
지연 폴링 주기(10초) 거의 실시간
수정 전 값 트리거에서 따로 저장해야 함 기본으로 같이 옴
선점, 상태 관리 직접 구현 (상태 코드 0/1/9, 선점, 재처리 횟수) 필요 없음. 어디까지 읽었는지 Debezium과 Kafka가 기록
필요한 것 DB 트리거 권한 DB 로그 접근 권한, Kafka Connect

동작상 차이는 세 가지다.

  • 기록하는 내용 — 트리거 테이블은 키만 기록하고 NiFi가 나중에 원본과 JOIN해서 데이터를 가져옴. Debezium은 바뀐 행 전체를 이벤트에 담아 보내므로 JOIN이 불필요함
  • 데이터 시점 — JOIN 방식은 NiFi가 조회하는 그 시점의 최신 값을 가져옴. Debezium은 변경이 일어난 순간의 값을 보냄
  • 진행 상태 관리 — 지금은 상태 코드, 선점, 재처리 횟수를 직접 관리함. Debezium은 Kafka 토픽에 이벤트를 쌓기만 하고, 어디까지 처리했는지는 Kafka를 읽는 쪽(consumer)의 offset으로 관리함. 따라서 재처리 로직도 읽는 쪽에서 만들어야 함

같은 행이 짧은 시간에 여러 번 바뀌면, 지금 방식은 마지막 값 한 번만 처리하고 Debezium은 변경마다 이벤트가 하나씩 옴

1.3 성능

Debezium이 유리한 이유는 다음과 같다.

  • 원본 DB 쓰기 부담 없음 — 트리거 방식은 INSERT 한 번에 트리거 테이블 INSERT가 한 번 더 일어남. 21만 건 적재가 느렸던 이유 중 하나
  • 폴링과 JOIN 조회 없음 — 지금은 10초마다 선점 UPDATE, JOIN 조회, 상태 UPDATE를 DB에 보냄. 처리량이 늘수록 이 쿼리들이 DB 부담이 됨
  • 짧은 지연 — 폴링 주기를 기다리지 않음

대신 생기는 부담도 있다.

  • Kafka Connect 운영 — Kafka Connect를 따로 운영해야 함. 인프라가 하나 늘어남
  • DB 디스크 위험 — Debezium이 멈추면 DB 디스크가 찰 수 있음. PostgreSQL은 Debezium이 아직 읽지 않은 WAL을 지우지 않고 계속 보관함(replication slot). 장애가 길어지면 DB 서버 디스크가 가득 찰 수 있어 모니터링 필수
  • 이벤트 수 증가 — 같은 행이 여러 번 바뀌면 바뀐 횟수만큼 이벤트가 옴
  • 초기 스냅샷 부담 — 큰 테이블은 처음에 전체를 한 번 읽어서 DB 부하가 큼
상황  적합한 방식
처리량이 크고 계속 늘어날 예정 Debezium
지금처럼 10초 폴링으로 충분한 양 트리거 방식도 문제없음

성능 이점은 분명하지만, 그 대가로 Kafka Connect 운영과 WAL 디스크 관리라는 새 책임이 생김

1.4 기존 NiFi 흐름 전환 시 영향

그대로 쓸 수 있는 부분(뒤쪽)은 다음과 같다.

  • 변환 — ScriptedTransformRecord의 refang과 암호화
  • 출력 형식 — JsonRecordSetWriter로 만드는 출력 형식
  • 전송 — 파일 이름 지정, PutSFTP
  • 로그 — 로그 테이블 INSERT 흐름

바뀌는 부분(앞쪽)은 다음과 같다.

  • 수집 단계 통합 — GenerateFlowFile → 선점 UPDATE → JOIN 조회 → 상태 UPDATE가 ConsumeKafkaRecord 하나로 바뀜
  • 필드 이름 확인 — 이벤트 필드 이름이 SQL 결과 컬럼 이름과 같은지 확인해야 함. 스크립트의 record.getValue("msg_incl_url")가 그대로 동작하려면 Debezium에서 ExtractNewRecordState로 평평하게 만들어 두는 게 좋음
  • UPDATE/DELETE 유입 — UPDATE, DELETE 이벤트도 들어옴. INSERT만 처리한다면 skipped.operations나 NiFi에서 걸러야 함

제일 크게 바뀌는 부분은 재처리와 추적이다.

  • 상태 구조 제거 — 트리거 테이블이 없어지므로 상태 코드(0/1/9), retry_cnt, current_flow_id 구조가 통째로 사라짐
  • offset 선기록 — ConsumeKafkaRecord는 메시지를 flowfile로 만든 뒤 Kafka에 "여기까지 읽었다"고 바로 기록함. 그 뒤 PutSFTP가 실패해도 Kafka는 그 메시지를 다시 주지 않음. 실패 건을 재처리용 토픽에 다시 넣는 방식 같은 새 재처리 구조가 필요함
  • 추적 기준 변경 — 로그 테이블의 추적 기준도 current_flow_id 대신 다른 값(Kafka offset이나 PK)으로 바꿔야 함

실제로 큰 작업은 NiFi 밖에 있음. PostgreSQL 전환, Kafka Connect 설치와 운영(폐쇄망 반입 포함), WAL과 slot 모니터링


2. Debezium 초기 구축에 필요한 지식

PostgreSQL + Debezium을 폐쇄망에 처음부터 올린다고 가정했을 때 알아야 할 Kafka, Kafka Connect, DB 설정, 운영 항목 정리

2.1 Kafka 기본 개념

Debezium은 Kafka 위에서 동작하므로 Kafka 기본 개념이 먼저 필요하다.

  • 토픽, 파티션, offset — 이벤트가 어디에 어떤 순서로 쌓이고, 어디까지 읽었는지 어떻게 기록하는지
  • 컨슈머 그룹 — 여러 소비자가 나눠 읽는 방식, 재처리할 때 offset을 되돌리는 방법
  • retention, replication factor — 얼마나 보관할지, 브로커가 죽어도 유실되지 않게 하는 방법
  • 클러스터 공유 여부 — 이미 PublishKafka로 쓰는 Kafka가 있다면 그 클러스터를 같이 쓸지 따로 둘지 결정

2.2 Kafka Connect

Debezium이 실제로 돌아가는 곳이다.

  • Worker, Connector, Task — Worker는 실행 서버, Connector는 "어느 DB를 읽을지" 설정, Task는 실제 작업 단위
  • standalone / distributed 모드 — 운영은 distributed가 기본. 설정과 진행 상태를 Kafka 내부 토픽 3개(config, offset, status)에 저장
  • REST API — 기본 8083 포트. 커넥터 생성, 상태 확인, 재시작을 모두 REST 호출로 함. GUI 없음
  • plugin.path — 커넥터 jar를 넣는 폴더. 폐쇄망이라 Debezium 압축 파일을 직접 받아 여기 풀어야 함
  • Converter — 이벤트를 JSON으로 보낼지 Avro로 보낼지. Avro는 Schema Registry 서버가 추가로 필요해서 처음에는 JSON이 간단함
  • SMT(Single Message Transform) — 이벤트를 Kafka에 넣기 전에 가공하는 기능. ExtractNewRecordState를 쓰면 before/after 구조를 걷어내고 바뀐 뒤의 행만 남겨 뒤쪽 처리가 단순해짐

2.3 PostgreSQL 쪽 설정

  • wal_level = logical — 바꾸려면 DB 재시작 필요
  • max_replication_slots, max_wal_senders — Debezium 커넥터 수만큼 여유
  • 전용 계정 — REPLICATION 권한 + 대상 테이블 SELECT 권한. pg_hba.conf에서 복제 접속 허용
  • publication — 어떤 테이블의 변경을 내보낼지 정하는 PostgreSQL 객체
  • REPLICA IDENTITY — 기본값이면 UPDATE/DELETE 때 before에 키 컬럼만 옴. 이전 값 전체가 필요하면 FULL
  • max_slot_wal_keep_size — PostgreSQL 13 이상. Debezium이 멈췄을 때 WAL이 무한정 쌓여 디스크가 가득 차는 걸 막는 안전장치. 꼭 설정

2.4 Debezium PostgreSQL 커넥터 핵심 설정

설정  의미
database.hostname, database.port, database.user, database.password, database.dbname 접속 정보
topic.prefix 토픽 이름 앞부분. 토픽은 "접두어.스키마.테이블" 형태로 만들어짐
plugin.name = pgoutput PostgreSQL 기본 내장 방식이라 추가 설치 불필요
slot.name, publication.name slot과 publication의 이름
table.include.list 변경을 잡을 테이블 목록
snapshot.mode 처음 연결할 때 기존 데이터를 전부 읽을지
heartbeat.interval.ms 변경이 거의 없는 테이블만 볼 때도 WAL이 쌓이지 않게 주기적으로 신호

2.5 운영

  • 상태 확인 — REST API로 커넥터와 Task 상태 조회, 실패하면 재시작
  • 지연 모니터링 — PostgreSQL의 pg_replication_slots에서 Debezium이 얼마나 뒤처졌는지 확인. 계속 늘어나면 디스크 위험 신호
  • 테이블 구조 변경 — 컬럼 추가/변경 시 이벤트 형식도 바뀜. 뒤쪽 소비자가 대응할 수 있어야 함
  • 재시작과 재처리 — 어디까지 읽었는지는 offset 토픽에 저장됨. 처음부터 다시 읽고 싶을 때 offset을 지우고 스냅샷을 다시 하는 절차를 알아 둘 것
  • 보안 — DB 비밀번호가 커넥터 설정에 평문으로 들어감. 파일 기반 config provider로 분리 가능

2.6 뒤쪽(소비) 연결

지금 NiFi 흐름으로 치면 트리거 테이블 폴링 부분이 ConsumeKafka(또는 ConsumeKafkaRecord)로 바뀐다.

  • op 분기 — op 값(c, u, d, r)에 따라 분기
  • tombstone 처리 — DELETE 뒤에 오는 빈 메시지 처리
  • 중복 안전성 — 같은 이벤트가 두 번 올 수 있다는 전제로 중복에 안전하게 처리

2.7 폐쇄망 추가 고려사항

  • Java 버전 — Debezium과 Kafka 버전마다 요구하는 Java 버전이 다름. 서버의 Java 버전 먼저 확인
  • 설치 파일 반입 — Kafka 압축 파일, Debezium PostgreSQL 커넥터 압축 파일(Maven Central 배포), PostgreSQL JDBC 드라이버. Docker를 쓴다면 이미지도 반입

2.8 추천 학습 순서

순서  내용
1 Kafka 단일 노드 + Kafka Connect standalone + PostgreSQL 테이블 하나로 최소 구성 띄우기
2 INSERT/UPDATE/DELETE를 해 보면서 토픽에 쌓이는 이벤트를 직접 보기
3 Debezium을 멈춰 두고 WAL이 쌓이는 걸 확인
4 NiFi ConsumeKafka로 읽어서 지금 흐름의 뒤쪽(변환, 암호화, 전송)에 붙여 보기

3단계는 운영에서 제일 중요한 위험을 직접 체감하는 과정이라 건너뛰지 않는 게 좋음


3. Kafka Connect의 역할과 클러스터 구조

Debezium의 실행 환경인 Kafka Connect의 개념, 필요성, 커넥터 종류, Worker 다중 구성과 연결 구조 정리

3.1 Kafka Connect

Kafka와 다른 시스템 사이에서 데이터를 옮겨 주는 실행 환경이다.

  • Kafka에 포함 — Apache Kafka를 받으면 같이 들어 있음 (bin/connect-distributed.sh)
  • 별도 실행 — 자동으로 켜지지 않고 따로 실행해야 함

3.2 Debezium에 Kafka Connect가 필요한 이유

DB 데이터를 Kafka에 넣으려면 원래는 DB를 읽고 Kafka에 쓰는 프로그램(프로듀서)을 직접 짜야 한다. Kafka Connect를 쓰면 이미 만들어진 커넥터를 설정만 해서 돌릴 수 있다.

  • 자동 처리 기능 — 어디까지 옮겼는지 기록, 실패 시 재시작, 여러 서버에 작업 분산은 Kafka Connect가 알아서 함
  • Debezium의 형태 — Debezium은 Kafka Connect용 커넥터로 만들어져 있음. 따라서 Debezium을 돌리려면 Kafka Connect가 필요함

3.3 커넥터 종류

종류  방향  예시
Source 커넥터 외부 → Kafka Debezium
Sink 커넥터 Kafka → 외부 토픽 내용을 DB, 파일 저장소, Elasticsearch 등에 넣음

3.4 NiFi와 비교

  • NiFi — 캔버스에 프로세서를 놓고 연결해서 데이터를 옮기는 도구
  • Kafka Connect — "Kafka로 넣기" 또는 "Kafka에서 빼기"만 전문으로 하는 도구. 화면 없이 설정으로만 운영하고, 중간 가공 기능은 NiFi보다 훨씬 단순함

3.5 Worker 다중 구성

  • 클러스터 — distributed 모드로 띄우면 여러 대가 하나의 Connect 클러스터가 됨
  • Worker — 각 서버에서 도는 Kafka Connect 프로세스. NiFi 노드 같은 개념
  • group.id — 설정 파일의 group.id가 같은 Worker끼리 한 클러스터로 묶임
  • 작업 분산과 장애 이전 — 커넥터와 Task를 Worker들에게 나눠서 실행. Worker 하나가 죽으면 그 작업이 다른 Worker로 자동 이동
  • 독립 클러스터 — group.id와 내부 토픽 이름을 다르게 주면 서로 독립된 Connect 클러스터를 여러 개 운영할 수도 있음
  • 커넥터당 Task 1개 — Debezium PostgreSQL 커넥터는 커넥터 하나당 Task가 하나. WAL은 순서대로 읽어야 해서 나눠 읽을 수 없음

Worker를 늘려도 커넥터 하나가 빨라지지 않음. Worker 여러 대는 주로 장애 대비용

3.6 클러스터 연결 구조

Worker끼리 직접 통신해서 조율하지 않는다. 모두 Kafka를 통해 조율한다.

          +--------------+
          | Kafka 브로커 |  <- 내부 토픽 3개 : config / offset / status
          +------+-------+
        +--------+--------+
     Worker1  Worker2  Worker3   (같은 group.id)
        |
   PostgreSQL (Debezium이 WAL을 읽음)
  • Kafka 접속 — 모든 Worker는 설정 파일의 bootstrap.servers로 Kafka에 접속
  • 내부 토픽 — 커넥터 설정은 config 토픽, 어디까지 읽었는지는 offset 토픽, 실행 상태는 status 토픽에 저장. 그래서 Worker가 바뀌어도 이어서 실행 가능
  • 리더 Worker — Worker 중 하나가 리더가 되어 "어느 Worker가 어느 커넥터를 맡을지" 나눠 줌
  • DB 접속 범위 — Debezium을 실행하는 Worker는 PostgreSQL에도 접속할 수 있어야 함. 어느 Worker가 맡을지 모르니 모든 Worker에서 DB 접속이 되도록 방화벽을 열어야 함

standalone 모드는 Worker 하나만 띄우고, 진행 상태를 Kafka가 아니라 로컬 파일에 저장한다.

모드 상태  저장 위치 용도
standalone 로컬 파일 테스트용. 간단하지만 그 서버가 죽으면 끝
distributed Kafka 내부 토픽 운영용

4. REST API와 Debezium 설정 방식

Kafka Connect를 조작하는 REST API의 개념, 주요 요청, ping·curl과의 차이, 파일 설정과 REST 설정의 구분 정리

4.1 REST API

URL로 요청을 보내서 프로그램을 조작하는 방식이다. 웹 브라우저가 URL로 웹 페이지를 요청하는 것과 같은 원리인데, 사람이 보는 화면 대신 JSON 데이터를 주고받는다.

  • Kafka Connect의 리모컨 — NiFi는 웹 화면이 있어서 마우스로 프로세서를 만들고 시작함. Kafka Connect에는 그런 화면이 없어서, 정해진 주소로 요청을 보내 명령함
HTTP 메소드 용도
GET 조회
POST 생성, 실행
PUT 수정
DELETE 삭제

4.2 Kafka Connect 주요 요청

요청  내용
GET /connectors 커넥터 목록 조회
POST /connectors 커넥터 생성 (설정 JSON을 같이 보냄)
GET /connectors/{이름}/status 커넥터와 Task 상태 조회
PUT /connectors/{이름}/config 설정 변경
POST /connectors/{이름}/restart 재시작
PUT /connectors/{이름}/pause, /resume 일시정지, 재개
DELETE /connectors/{이름} 삭제

터미널에서는 보통 curl로 보낸다.

curl http://<Worker주소>:8083/connectors/<커넥터이름>/status
  • 요청 대상 — 클러스터에서는 아무 Worker에나 요청을 보내면 됨. 필요하면 리더에게 알아서 전달

NiFi 웹 화면도 내부적으로 NiFi REST API를 호출해서 동작함. 운영 NiFi의 #{today} 파라미터를 매일 바꿔 주는 작업도 NiFi REST API를 호출하는 스크립트일 가능성이 큼

4.3 ping, curl과의 차이

ping은 REST API가 아니다.

  • ping — "이 주소의 컴퓨터가 네트워크에서 응답하나?"만 확인. ICMP라는 네트워크 수준 신호를 보내고 돌아오는지만 봄. 특정 프로그램에 명령하는 게 아님
  • REST API — HTTP로 특정 프로그램에게 URL로 요청을 보내 "이거 만들어", "상태 알려줘" 같은 명령을 하고 결과(JSON)를 받음
ping 10.0.0.5
-> 10.0.0.5 서버가 켜져 있고 네트워크로 연결되는지만 확인

curl http://10.0.0.5:8083/connectors
-> 10.0.0.5 서버 8083번 포트의 Kafka Connect에게 "커넥터 목록 알려줘"라고 요청

Kafka Connect에 접속이 안 될 때는 ping으로 서버가 살아 있는지 먼저 보고, 살아 있으면 curl로 Kafka Connect 프로그램이 응답하는지 확인

curl이 곧 REST API인 것도 아니다. curl은 REST API를 호출하는 도구 중 하나다.

  • REST API — 프로그램(Kafka Connect)이 받아 주는 요청 규칙. "이 주소로 이런 형식의 요청을 보내면 이렇게 처리해 준다"는 약속
  • curl — 터미널에서 HTTP 요청을 보내는 프로그램
  • 식당 비유 — REST API는 주문받는 창구와 메뉴판, curl은 주문하는 방법 중 하나
  • 다른 호출 도구 — 같은 REST API를 웹 브라우저(조회만), Postman, Python requests, NiFi InvokeHTTP로도 호출 가능
  • curl의 다른 용도 — 반대로 curl은 REST API가 아닌 곳(파일 다운로드 등)에도 쓸 수 있음

4.4 Debezium 설정 방식

distributed 모드라면 Debezium 설정은 REST API로 한다. 단, 설정이 두 층으로 나뉜다.

층 설정 방법 내용 반영 방식
Kafka Connect 노드 자체 파일 (connect-distributed.properties) Kafka 주소, group.id, 플러그인 폴더 위치 등 파일 수정 후 Kafka Connect 재시작
Debezium 커넥터 REST API 어느 PostgreSQL의 어느 테이블을 볼지 등 재시작 없이 새 설정 전송

커넥터 설정은 Kafka Connect가 켜진 뒤 REST API로 보낸다.

curl -X POST http://<Kafka Connect주소>:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "pg-connector",
    "config": {
      "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
      "database.hostname": "<PostgreSQL주소>",
      "database.port": "5432",
      "database.user": "<계정>",
      "database.password": "<비밀번호>",
      "database.dbname": "<DB이름>",
      "topic.prefix": "<토픽 접두어>",
      "plugin.name": "pgoutput",
      "table.include.list": "<스키마>.<테이블>"
    }
  }'
  • 커넥터 이름 — "pg-connector"는 예시. 원하는 이름 사용
  • 즉시 실행 — 보내면 Kafka Connect가 설정을 저장하고 바로 Debezium을 실행
  • 설정 변경 — 설정을 바꿀 때도 재시작 없이 REST API로 새 설정을 보내면 됨

standalone 모드(테스트용)라면 커넥터 설정도 파일로 만들어 실행할 때 같이 넘길 수 있다.

bin/connect-standalone.sh config/connect-standalone.properties pg-connector.properties

5. 전체 구성도

PostgreSQL → Kafka Connect(Debezium) → Kafka → NiFi로 이어지는 전체 구조와 이해 내용 점검

5.1 이해 내용 점검

이해한 내용 판정   보충
Kafka를 구현하면 Kafka Connect도 같이 쓸 수 있다 맞음 Kafka를 설치하면 Kafka Connect 프로그램도 같이 들어 있음. 단, 자동으로 켜지지 않고 따로 실행
띄우면 노드 같은 것이다 맞음 실행한 Kafka Connect 하나가 노드 하나. NiFi 노드처럼 여러 대를 클러스터로 묶을 수 있음
여기에 Debezium을 깐다 맞음 Debezium 파일(jar)을 Kafka Connect의 플러그인 폴더에 넣으면 Kafka Connect가 Debezium을 실행 가능
Debezium이 외부 데이터를 Kafka 토픽으로 가져온다 맞음 —
Debezium은 PostgreSQL에 접근할 수 있어야 한다 맞음 네트워크로 연결되어야 하고, 접속 계정과 권한도 있어야 함

5.2 보충

  • Kafka 옆에서 동작 — "Kafka 위에"라기보다 "Kafka 옆에". Debezium은 Kafka 브로커 안이 아니라 Kafka Connect라는 별도 프로세스 안에서 돌고, Kafka Connect가 Kafka에 접속해서 토픽에 넣음
  • WAL 읽기 — 테이블을 지켜보는(주기적으로 조회하는) 게 아니라 WAL(변경 로그)을 읽음. PostgreSQL이 변경을 기록하는 로그를 따라 읽다가, 대상 테이블의 변경이 나오면 이벤트로 만듦
  • 다중 테이블 — 커넥터 하나로 여러 테이블을 볼 수 있음. 테이블마다 토픽이 따로 생김

5.3 구성도

[PostgreSQL]
     |  Debezium이 변경 내용(WAL)을 읽어 감
     v
[Kafka Connect 노드]  <- 안에 Debezium 설치
     |  읽은 내용을 토픽에 넣음
     v
[Kafka]
     |
     v
[NiFi]  토픽을 읽어서 처리

흐름으로 쓰면 다음과 같다.

PostgreSQL 테이블 변경
  -> WAL에 기록
  -> Kafka Connect 안의 Debezium이 WAL을 읽음
  -> 테이블별 Kafka 토픽에 이벤트 저장
  -> NiFi 등이 토픽을 읽어서 처리

6. 초기 설정 범위

최소 동작 설정부터 운영 수준 설정까지, 결정해야 할 항목을 영역별로 정리

돌아가게만 하는 데는 10줄 정도면 충분하다. "입맛대로" 맞춘 운영 설정은 보통 20~30개 항목을 정하게 된다.

설정 개수보다 "결정할 게 무엇인지" 아는 게 중요함

6.1 최소 설정

connector.class    = io.debezium.connector.postgresql.PostgresConnector
database.hostname / database.port / database.user / database.password / database.dbname
topic.prefix       = 토픽 이름 앞부분
plugin.name        = pgoutput
table.include.list = 볼 테이블
  • 기본값 주의 — 이것만 넣으면 돌아가지만 아래 기본값들이 그대로 적용됨. 기본값 중에는 원하지 않는 동작이 꽤 있음

6.2 수집 대상

결정할 것 설정
어떤 테이블? table.include.list (또는 schema.include.list)
어떤 컬럼? 민감한 컬럼 제외 column.exclude.list
민감한 컬럼을 가리거나 해시로 보낼지 column.mask.with.<N>.chars, column.mask.hash.<알고리즘>.with.salt.<salt>
어떤 변경? 예: INSERT만 필요 skipped.operations (예: u,d 면 UPDATE/DELETE 제외)
UPDATE/DELETE 때 이전 값 전체 필요? PostgreSQL 테이블의 REPLICA IDENTITY FULL (DB 쪽 설정)
  • INSERT 전용 — 지금 트리거 테이블처럼 INSERT만 필요하면 skipped.operations로 줄일 수 있음
  • 민감 값 처리 — URL 같은 민감한 값은 Kafka에 들어가기 전에 가리거나 뺄 수 있음

6.3 최초 연결 동작

결정할 것  설정
기존 데이터를 전부 보낼지, 변경분만 보낼지 snapshot.mode (initial = 전부 보내고 시작, 변경분만 = 버전에 따라 no_data 또는 never)
나중에 특정 테이블만 다시 전부 보내고 싶을 때 증분 스냅샷 (signal.data.collection) — 재처리에 유용

6.4 메시지 모양

뒤쪽 NiFi 처리에 직접 영향을 주는 영역이다.

결정할 것 설정
메시지마다 스키마 정보를 붙일지 value.converter.schemas.enable (true면 메시지가 몇 배 커짐. JSON이면 보통 false)
before/after 그대로? 바뀐 뒤 행만 평평하게? SMT ExtractNewRecordState (평평하게 만들어 줌)
평평하게 만들 때 op, 테이블 이름 등을 남길지 ExtractNewRecordState의 add.fields
DELETE 표현 방식, 빈 메시지(tombstone) 여부 tombstones.on.delete, 삭제 처리 옵션(버전마다 이름 다름)
NUMERIC 컬럼 표현 방식 decimal.handling.mode (string 또는 double 권장)
날짜/시간 표현 방식 time.precision.mode

decimal.handling.mode는 꼭 정할 것. 기본값(precise)이면 JSON에서 숫자가 알아보기 힘든 base64 문자열로 나옴. 처음 써 보는 사람이 가장 많이 당황하는 부분

6.5 토픽 구성

결정할 것 설정
토픽 이름 규칙 topic.prefix + 필요하면 SMT로 이름 변경(RegexRouter 등)
여러 테이블을 토픽 하나로 모을지 SMT ByLogicalTableRouter
토픽 파티션 수, 복제 수 topic.creation.default.partitions, topic.creation.default.replication.factor

6.6 안정성과 운영

결정할 것 설정
replication slot, publication 이름 slot.name, publication.name
publication을 Debezium이 만들지, DBA가 만들지 publication.autocreate.mode
변경이 드문 테이블에서 WAL이 쌓이지 않게 heartbeat.interval.ms
커넥터를 멈출 때 slot을 지울지 slot.drop.on.stop (운영에서는 반드시 false)
처리량 조정 max.batch.size, max.queue.size, poll.interval.ms
DB 접속 암호화 database.sslmode
비밀번호를 설정에 평문으로 안 넣기 Kafka Connect의 config provider (${file:경로:키} 형태)

6.7 Kafka Connect 노드 설정 (파일)

  • 접속과 클러스터 — bootstrap.servers, group.id
  • 내부 토픽 — 내부 토픽 3개(config/offset/status)의 이름과 복제 수
  • 기타 — 기본 converter, plugin.path, REST 포트

6.8 PostgreSQL 쪽 설정

  • WAL — wal_level = logical (재시작 필요), max_replication_slots, max_wal_senders
  • 디스크 보호 — max_slot_wal_keep_size
  • 접속과 권한 — 복제 전용 계정, pg_hba.conf
  • 테이블 단위 — 테이블별 REPLICA IDENTITY

6.9 설정 순서

  • 최소 설정 먼저 — 처음에는 6.1 최소 설정으로 띄우고, 6.4(메시지 모양)를 먼저 맞추는 걸 추천. 뒤쪽 NiFi에서 받는 모양이 정해져야 나머지 흐름을 설계할 수 있음
  • 디스크 보호 필수 — 운영에 올리기 전에 max_slot_wal_keep_size, heartbeat.interval.ms, slot.drop.on.stop은 반드시 챙길 것

요약

구분  핵심 내용
Debezium DB 트랜잭션 로그(PostgreSQL은 WAL)를 읽어 변경 이벤트를 Kafka로 보내는 CDC 도구
트리거 방식 대비 원본 DB 쓰기 부담·폴링·JOIN이 없고 지연이 짧음. 대신 Kafka Connect 운영, WAL 디스크 위험, 이벤트 수 증가
NiFi 전환 영향 뒤쪽(변환·암호화·전송)은 유지, 앞쪽 수집은 ConsumeKafkaRecord로 대체, 재처리·추적 구조는 새로 설계
Kafka Connect Debezium의 실행 환경. Worker는 Kafka 내부 토픽 3개로 조율, 커넥터당 Task 1개
REST API Kafka Connect를 조작하는 HTTP 인터페이스. 노드 설정은 파일, 커넥터 설정은 REST API
초기 설정 최소 10줄로 동작. 메시지 모양(ExtractNewRecordState, decimal.handling.mode)을 먼저 확정
운영 필수 max_slot_wal_keep_size, heartbeat.interval.ms, slot.drop.on.stop=false, replication slot 지연 모니터링

Debezium 도입의 핵심은 커넥터 설정보다 운영 구조에 있다. Kafka Connect를 띄우고 메시지 모양을 확정한 뒤, WAL이 쌓이지 않도록 지키는 장치를 먼저 갖추는 것이 순서다