기존 시스템의 한계와 개편 필요성
시스템 개편을 하면서 새로운 아키텍처를 구상해야 하는 과제가 있었습니다.
오랫동안 DB 링크와 트리거 기반의 데이터 연계 시스템(ESB)을 운영해 왔었는데, 기존 방식은 다음과 같았습니다.
Source DB → A DB 채널 → A DB target table
├── → B DB 채널 → B DB target table
└── → C DB 채널 → C DB target table
해당 데이터 연계를 진행하려면 복잡한 단계를 거쳐야 했습니다:
- 트리거 작성
- 로직 체크(INSERT/UPDATE/DELETE 구분)
- 비즈니스 로직 구현 (최대한 지양했지만 불가피한 경우 존재)
- 소스 DB와 타겟 DB의 Getter/Setter 생성
- 각 DB별 접속 및 데이터 처리 로직 개발
- Mapper SQL 개발
- 소스-타겟 간 데이터 매핑 쿼리 작성
- 신규 DB 연계 시 ActiveMQ 채널 생성
- 애플리케이션 배포 및 테스트
- 연계 로직 검증 및 운영 반영
기존 방식의 문제점
문제는 다음과 같은 상황에서 복잡도가 기하급수적으로 증가했다는 점입니다:
- 이기종 DB 연계: Oracle ↔ PostgreSQL, MySQL 등
- PK 구조 차이: 서로 다른 키 구조로 I/U/D 처리 복잡
- SQL 방언 차이: 각 DB별 문법 및 함수 차이
- 비즈니스 로직 변경: 트리거 로직 수정 시 영향도 파악 어려움
물론 데이터 하나하나 모두 읽어서 처리를 하는 시스템이었기 때문에 안정성에 대해서는 보장이 되었지만, 결과적으로 테이블 하나를 연동하는데 최소 3시간에서 하루까지 소요가 되어 개발 비용이 높다는 문제가 있었습니다.
또한 아키텍처적 관점에서의 역할 분리도 중요했는데요.
데이터 연계 애플리케이션에 비즈니스 로직이 포함되는 문제가 있었습니다.
- 데이터 전송 과정에서 비즈니스 로직 처리
- 데이터 변환, 검증, 필터링 등의 로직이 연계 시스템에 산재
- 로직 변경 시 데이터 팀이 직접 수정해야 하는 구조
근본적인 문제점들:
1. 책임 소재 불분명:
- 비즈니스 로직 오류 -> 시스템 담당자 책임? 백엔드팀 책임?
- 데이터 품질 이슈 -> 어느 팀에서 해결을 해야 할까?
2. 유지보수의 어려움:
- 로직 변경 시 여러 팀 간 협업 필요
- 장애 발생 시 원인 파악이 힘듦
- 데이터 모니터링은 시스템 담당자가 하고 이슈 파악을 백엔드 담당자가 하지 못함
이러한 문제들을 해결하기 위해 데이터 이동에는 로직을 넣지 않고, 백엔드에서 처리하자고 의견 제시를 하였고 시스템 구상을 마친 지금도 현재 그렇게 진행을 하고 있습니다.
새로운 책임 분리:
- 데이터팀: 순수한 데이터 전송 담당 (CDC, 파이프라인 운영, 인프라)
- 백엔드팀: 비즈니스 로직 및 데이터 변환 담당 및 각 도메인 파트에 대한 책임
이것이 바로 Kafka connect + CDC 도입의 핵심 철학이었습니다. 단순히 기술적 개선이 아니라, 조직의 책임과 역할을 명확히 분리하기 위한 아키텍처 결정이었습니다.
새로운 아키텍처의 필요성
이번 시스템 개편을 진행하면서 DB 성능 저하와 복잡한 의존성들을 모두 제거하기 위해 "트리거를 모두 제거하는 방향으로 간다"라는 지침이 있었습니다.
이에 따라 1:1 CDC(Change Data Capture) 방식으로 데이터 연계 방식을 전환하기로 했습니다.
핵심 원칙은 다음과 같습니다.
- 원본 데이터 보존: DB 데이터를 변환 없이 그대로 전송
- 타겟에서 변환: 변환이 필요한 데이터는 안전하게 타겟으로 넘긴 후 타겟에서 처리
- 느슨한 결합: 소스와 타겟 간 직접적인 의존성 제거
- 양방향 데이터 전송이 필요한 경우: 모든 데이터는 소스 데이터를 기준으로 수정하여 처리
기존 vs 새로운 방식 비교
기존 방식 (채널 기반)
Source DB → [트리거 + 로직] → 채널(Queue) → Target DB
↓
메시지 소멸
기존 방식은 각 타겟 DB에 대한 채널을 만들어서 큐 방식으로 타겟에 전송하고 메시지가 소멸되는 구조였습니다.

새로운 방식 (Kafka 기반)
Source DB → [CDC](source connector) → Kafka Topic → sink connecter -> Target DB
↓
데이터 보존 + 재사용

왜 Kafka인가?
데이터를 1:1로 보내되 보존에 대한 안정성도 높이며 전달할 수 있는 시스템이 무엇일까 고민하다가 메시징 시스템으로 Kafka를 선택하게 되었습니다.
Kafka 선택 이유:
- 데이터 보존: 설정된 보관 기간 동안 메시지 유지
- 다중 Consumer: 하나의 토픽을 여러 Consumer가 독립적으로 소비
- 확장성: 파티션을 통한 수평 확장
- 안정성: 복제를 통한 내결함성
- 순서 보장: 파티션 내에서 메시지 순서 보장
1. Kafka Connect란 무엇인가?
Kafka Connect의 핵심 개념
Kafka Connect는 Apache Kafka와 외부 시스템(데이터베이스, 파일 시스템, 검색 엔진 등) 간의 데이터를 안정적이고 확장 가능하게 스트리밍하기 위한 프레임워크입니다.
왜 Kafka Connect인가?
기존에는 각 시스템마다 개별적인 데이터 파이프라인을 구축했어야 했어요. A 시스템에서 B 시스템으로, B에서 C로, C에서 D로... 이런 식으로 말이죠. 하지만 Kafka Connect는 허브 앤 스포크(Hub and Spoke) 모델을 제공합니다.
Before: A ↔ B ↔ C ↔ D (복잡한 point-to-point 연결)
After: A ↘ ↙ C
Kafka
B ↗ ↖ D
Kafka Connect의 주요 장점:
- 표준화된 인터페이스: 커넥터라는 표준 컴포넌트로 다양한 시스템 연동
- 내결함성: 분산 모드로 운영하면 워커 노드 장애에도 자동 복구
- 확장성: 커넥터별로 태스크 수 조정으로 처리량 확장 가능
- 운영 편의성: REST API로 커넥터 생성/수정/삭제 관리
- 스키마 지원: Schema Registry와 연동하여 데이터 스키마 버전 관리
Source Connector vs Sink Connector
- Source Connector: 외부 시스템 → Kafka Topic
- Sink Connector: Kafka Topic → 외부 시스템
저희는 주로 Source Connector(Debezium)로 DB 변경사항을 Kafka에 적재하고, Sink Connector(JDBC Sink, Custom Consumer)로 타겟 시스템에 전파하는 구조로 운영하고 있습니다.
2. 실시간 DB 데이터 적재 방법론과 Debezium
DB 데이터를 실시간으로 Kafka에 적재하는 방법들
실시간으로 DB 변경사항을 Kafka에 반영하는 방법은 크게 3가지가 있습니다.
2.1 폴링(Polling) 방식 + 트리거
동작 원리:
- DB 테이블에 트리거를 달아서 변경 사항이 생기면 별도 Change Log 테이블에 기록합니다
- Kafka Connect가 주기적으로 Polling을 하여 Change Log 테이블을 확인 (JDBC Source Connector 로 사용할 수 있음)
- 조회된 데이터를 Kafka Topic으로 전송
장점:
- 구현이 비교적 단순함 (기존 시스템 방식)
- 대부분의 DB에서 지원
단점:
- 운영 DB에 추가 부하 (트리거 실행 + 폴링 쿼리)
- 트리거 관리 복잡성 (스키마 변경 시 트리거 수정 필요)
- 폴링 주기에 따른 지연 발생
- Change Log 테이블 용량 관리 필요
- TimeStamp에 따른 데이터 유실을 주의해야 함
2.2 CDC(Change Data Capture) - Debezium
동작 원리:
- DB의 트랜잭션 로그(PostgreSQL WAL, Oracle Redo Log, MySQL Binlog)를 직접 읽어서 변경사항 추출
- 로그 기반이므로 운영 DB에 추가 부하 최소화
- 실시간에 가까운 변경사항 감지
장점:
- 운영 DB 부하 최소화: 로그만 읽으므로 DB 성능에 거의 영향 없음
- 실시간성: 트랜잭션 로그를 실시간으로 읽어서 지연 최소
- 완전성: 모든 변경사항(INSERT/UPDATE/DELETE) 보장
- 스키마 진화: 테이블 구조 변경도 자동으로 감지
단점:
- DB별로 설정이 복잡 (WAL 설정, 권한 관리 등)
- 초기 학습 곡선이 높음
- DB 버전별 호환성 이슈
2.3 Apache Flink CDC
동작 원리:
- Flink의 CDC Connector를 사용하여 DB 변경사항을 스트림으로 처리
- Debezium과 유사하게 트랜잭션 로그 기반
장점:
- 실시간 스트림 처리와 CDC를 한 번에
- 복잡한 데이터 변환 로직 처리 가능
단점:
- Flink 클러스터 운영 복잡성 추가
- 단순 CDC만 필요할 때는 오버스펙
해당 조사를 마친 이후 저희는 다음과 같은 이유로 Debezium을 선택하였습니다.
- 운영 DB 부하 최소화: 기존 트리거 방식 대비 DB 성능 영향 거의 없음
- 실시간성: 트랜잭션 로그 기반으로 지연 시간 최소 (평균 3초 이내)
- 완전성: 모든 DML 작업을 빠짐없이 캡처
- 커뮤니티: 활발한 오픈소스 커뮤니티와 풍부한 문서
- 통합성: Kafka Connect 생태계와 완벽 호환
현재 Aurora PostgreSQL과 AWS RDS Oracle에 사용하기 위해 debezium source connector pg와 oracle을 도입하여 사용 중에 있습니다.
3. Aurora PostgreSQL과 RDS Oracle 사전 설정
3.1 Aurora PostgreSQL CDC 설정
Debezium을 다루면서 가장 애를 먹었던 부분인데요. 각 DB마다 내부 구성이 다르고 Replication에 대한 지식도 알고 있어야 해서 쉽게 바로 처리가 될 수 없었던 부분이었습니다. 해당 부분은 DB 관련으로 더 자세하게 포스팅 하려고 합니다.
WAL(Write-Ahead Logging) 설정
Aurora PostgreSQL에서 CDC를 사용하려면 먼저 WAL을 논리적 복제 모드로 설정해야 합니다
파라미터 그룹 설정:
wal_level = logical # 논리적 복제 활성화
max_replication_slots = 20 # 복제 슬롯 최대 개수
max_wal_senders = 20 # WAL 전송 프로세스 최대 개수
주의사항:
- 이 설정들은 클러스터 재시작이 필요합니다
- max_replication_slots는 생성할 커넥터 수보다 여유있게 설정
- WAL 용량 증가에 따른 스토리지 모니터링 필요
권한 설정
-- CDC 전용 사용자 생성
CREATE USER debezium WITH PASSWORD 'secure_password';
-- Aurora PostgreSQL 전용 복제 권한
GRANT rds_replication TO debezium;
-- 스키마 및 테이블 접근 권한
GRANT USAGE ON SCHEMA target_schema TO debezium;
GRANT SELECT ON ALL TABLES IN SCHEMA target_schema TO debezium;
-- 향후 생성될 테이블에 대한 기본 권한
ALTER DEFAULT PRIVILEGES IN SCHEMA target_schema
GRANT SELECT ON TABLES TO debezium;
Replication 및 Publication 관리
해당 부분에서 애를 많이 먹었는데, 결론적으로는 Source Connector를 생성할 때 replication과 publication name을 명시적으로 설정을 해 준 뒤 실행을 시키면 debezium이 자동으로 관리를 해 줍니다. 그것이 DB에 부하를 주지 않고 빠르고 안전하게 인프라를 구성할 수 있는 방법입니다.
특히, 해당 부분이 제대로 되지 않으면 DB에서 WAL 의 사이즈 증가와 lag 현상, 데이터 적재 불가능 등의 이슈가 생깁니다.
3.2 RDS Oracle CDC 설정
Oracle CDC는 PostgreSQL보다 설정이 복잡해요. LogMiner 기능을 사용하기 때문입니다.
ARCHIVELOG 모드 활성화
Oracle LogMiner는 아카이브 로그를 읽어야 하므로 ARCHIVELOG 모드가 필수입니다.
-- 현재 로그 모드 확인
SELECT log_mode FROM v$database;
-- NOARCHIVELOG면 다음 단계로 변경 (RDS는 자동화 도구 사용 권장)
-- 1. 인스턴스 중지
-- 2. 자동 백업 활성화 (ARCHIVELOG 활성화됨)
-- 3. 인스턴스 시작
Supplemental Logging 설정
LogMiner가 변경사항을 정확히 추적하려면 추가 로그 정보가 필요해요.
-- 최소 보조 로깅 활성화
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
-- PK/Unique Index 컬럼 로깅 (CDC에 필수)
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (UNIQUE INDEX) COLUMNS;
-- 설정 확인
SELECT supplemental_log_data_min, supplemental_log_data_pk, supplemental_log_data_ui
FROM v$database;
-- 모든 값이 'YES'여야 함
CDC 사용자 권한 설정
-- CDC 전용 사용자 생성
CREATE USER dbz_user IDENTIFIED BY "secure_password";
GRANT CONNECT, RESOURCE TO dbz_user;
-- LogMiner 관련 권한
GRANT LOGMINING TO dbz_user; -- LogMiner 사용 권한
GRANT SELECT ANY TRANSACTION TO dbz_user; -- 트랜잭션 조회
GRANT SELECT ANY DICTIONARY TO dbz_user; -- 데이터 딕셔너리 조회
-- LogMiner 관련 뷰 권한 (동의어 주의: V_$는 V$의 public 동의어)
GRANT SELECT ON V_$LOGMNR_CONTENTS TO dbz_user;
GRANT SELECT ON V_$LOG TO dbz_user;
GRANT SELECT ON V_$LOGFILE TO dbz_user;
GRANT SELECT ON V_$ARCHIVED_LOG TO dbz_user;
-- 대상 스키마 테이블 조회 권한
GRANT SELECT ON target_schema.target_table TO dbz_user;
Oracle 권한 관리:
- DBA 권한이 필요한 설정들이 많아서 운영팀과 미리 협의가 필요합니다
- 권한이 부족하면 LogMiner 초기화 단계에서 에러 발생
- 테스트 환경에서 충분히 검증 후 운영 적용
상대적으로 AWS RDS oracle 같은 경우에는 시스템 설정만 잘해 두면 oracle source connector을 pause, start를 해도 무리 없이 잘 진행이 되었고 끊긴 부분도 잘 캡처해서 데이터 유실 없이 토픽에 잘 적재 되었습니다.
postgres source connector의 경우에는 publication table 등록을 debezium에게 일임하여 자동으로 관리해 주는 것이 중요하였고, table list에 필요한 테이블만 넣어 데이터를 잘 읽고 커밋할 수 있도록 하는 것이 가장 중요한 부분이었습니다.
해당 부분은 트러블 슈팅을 비롯한 각종 에러 사항들을 따로 취합하여 포스팅할 예정입니다.
4. EC2에서 Kafka Connect 서버 구성 (Docker)
4.1 인프라 구성 개요
kafka connect를 도입하기로 결정했지만, 어떻게 운영할지는 또 다른 고민이었습니다.
컨플루언트는 관리형 서비스로 편하지만 비용이 상상을 초월할 정도로 만만치 않았습니다. 저희처럼 커넥터 시스템이 중요한 상황에서는 맞지 않는다고 판단하였습니다. 단순 카프카만 사용한다면 컨플루언트도 좋은 선택지 같습니다.
MSK Connect도 AWS 관리형이라 가장 좋은 선택지였지만 커넥터 한 대에 92 달러라는 것이 부담스러운 가격이었습니다. sink 커넥터의 경우 타겟 테이블마다 싱크 커넥터를 만들어야 하는데 사백 개가 넘는 커넥터를 하나에 92 달러를 주고 사용할 수는 없으니까요.
따라서 해당 사항들을 모두 PoC 하고 가격 비교를 해 본 결과 자체적으로 커넥터 서버를 구성해서 운영하는 것이 가장 저렴하다는 것을 판단하여 ec2 세 대로 고가용성을 보장하여 kafka connect 서버를 구축하게 되었습니다.
4.2 EC2 사양 선정: 뭘 우선해야 할까?
Kafka Connect 서버는 데이터를 전송하는 플랫폼이기 때문에 EC2 사양을 고를 때 고려해야 할 포인트들이 있습니다.
핵심 고려사항
1. 데이터 전송량이 높다면 → 네트워크 대역폭 우선
- Kafka Connect는 I/O 집약적인 작업이에요
- 특히 초기 스냅샷 시에는 대량 데이터 전송 발생
- 네트워크 성능이 좋은 인스턴스 타입 선택 필요
2. CPU vs 메모리, 뭐가 더 중요할까?
메모리 우선 상황:
- 커넥터 수가 많을 때 (10개 이상)
- 스냅샷 처리하는 테이블이 클 때
- 복잡한 변환(SMT) 로직이 많을 때
CPU 우선 상황:
- 높은 처리량(TPS)이 필요할 때
- 실시간 처리 지연을 최소화해야 할 때
- JSON 직렬화/역직렬화 부하가 클 때
사양 선정 기준
규모별 권장 사양:
| 소규모 (커넥터 1-5개, 일 처리량 < 100만건) → t3.large (2vCPU, 8GB RAM) - 테스트/개발 환경에 적합 - 비용 효율적이지만 프로덕션에는 부족할 수 있음 |
| 중규모 (커넥터 5-15개, 일 처리량 100만-1000만건) → m5.xlarge (4vCPU, 16GB RAM) - 안정적인 성능과 비용의 균형점 - 대부분의 운영 환경에 적합 |
| 대규모 (커넥터 15개+, 일 처리량 1000만건+) → m5.2xlarge (8vCPU, 32GB RAM) 이상 - 메모리 집약적 작업에 대응 - 동시 처리 성능 확보 |
각자 데이터 연계에 대한 규모를 고려하여 사양을 선정하는 것이 가장 중요합니다.
저 같은 경우에는 규모가 예상되지 않아 기본적인 사양으로 2vCPU에 8GB RAM 세 대를 구성하여 테스트 운영을 해 보면서 부하가 있을 시 ec2 사양을 스케일업 하려는 생각으로 초기 세팅은 그렇게 진행하였습니다.
4.3 Docker Compose 설정 및 실제 운영 경험
실제 운영 중인 docker-compose.yml 구성을 간단히 소개하면 다음과 같습니다.
version: '3.8'
services:
kafka-connect:
image: debezium/connect:3.0.0.Final
container_name: kafka-connect
network_mode: "host"
restart: unless-stopped
ports:
- "8083:8083" # Connect REST API
- "9012:9012" # JMX Monitoring
environment:
# MSK 클러스터 연결
BOOTSTRAP_SERVERS: "msk-broker-endpoints:9098"
GROUP_ID: "connect-prod"
# Connect 내부 토픽
CONFIG_STORAGE_TOPIC: "connect-configs"
OFFSET_STORAGE_TOPIC: "connect-offsets"
STATUS_STORAGE_TOPIC: "connect-status"
# MSK IAM 인증 설정
CONNECT_SECURITY_PROTOCOL: "SASL_SSL"
CONNECT_SASL_MECHANISM: "AWS_MSK_IAM"
# ...
# JMX 모니터링
KAFKA_JMX_OPTS: >-
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9012
-XX:+UseG1GC
4.4 실제 운영 결과와 느낀 점
현재 운영 규모:
- Source 커넥터: 6개 (PostgreSQL 5대, Oracle 1대, 개발 서버 별도)
- Sink 커넥터: 300개 이상
- 인스턴스 사양: 2vCPU, 8GB RAM × 3대
운영 중 발견한 문제점:
- 평상시에는 안정적으로 동작
- 대용량 데이터 유입 시 한계 드러남: 몇 억 건의 데이터가 한 번에 들어올 때 시스템이 버티지 못하는 상황 발생
교훈: 초기에는 보수적으로 시작해도 되지만, 실제 데이터 패턴을 모니터링하면서 스케일 업하는 것이 중요합니다.
5. Source Connector 설정
5.1 PostgreSQL Source Connector
{
"name": "source-dev-userdb",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"topic.prefix": "dev-userdb",
"database.hostname": "aurora-endpoint",
"database.port": "5432",
"database.user": "debezium",
"database.password": "secure_password",
"database.dbname": "userdb",
"plugin.name": "pgoutput",
"schema.include.list": "public,user_data",
"table.include.list": "public.users,user_data.profiles",
"slot.name": "dbz_dev_userdb", (중요)
"publication.name": "dbz_publication", (중요)
"snapshot.mode": "never", (중요)
"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState"
}
}
5.2 Oracle Source Connector
{
"name": "source-prod-oracle-erp",
"config": {
"connector.class": "io.debezium.connector.oracle.OracleConnector",
"topic.prefix": "prod-erpdb",
"database.url": "jdbc:oracle:thin:@//oracle-host:1521/ORCLPDB1",
"database.user": "dbz_user",
"database.password": "secure_password",
"schema.include.list": "ERP_SCHEMA",
"table.include.list": "ERP_SCHEMA.ORDERS,ERP_SCHEMA.CUSTOMERS",
"log.mining.strategy": "online_catalog",
"snapshot.mode": "initial",
"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState"
}
}
6. 운영 성과와 향후 계획
현재까지의 성과
정량적 개선:
- 개발 시간 단축: 테이블 하나 연동 시간이 하루 → 10분 이내
- DB 성능 개선: 트리거 제거로 운영 DB 부하 감소
- 실시간성 향상
정성적 개선:
- 책임 분리 명확화: 데이터팀은 파이프라인, 백엔드팀은 비즈니스 로직
- 장애 격리: 시스템 간 느슨한 결합으로 장애 영향 최소화
- 확장성 확보: 새로운 Consumer 추가 시 간단한 설정만으로 가능
아직 남은 과제들
- 대용량 처리 최적화: 억 단위 데이터 유입 시 안정성 확보
- 모니터링 체계 강화: 더 세밀한 성능 지표 및 알림 시스템
- Schema Registry 도입: 스키마 버전 관리 체계화
마무리
트리거 기반의 복잡한 데이터 연계 시스템에서 Kafka Connect + Debezium 기반의 CDC 파이프라인으로 전환하는 여정을 정리해 보았습니다.
단순히 기술적 개선이 아니라 조직의 책임과 역할을 명확히 분리하려는 아키텍처 결정이었고, 결과적으로는 만족스러운 성과를 얻고 있습니다.
물론 아직 완벽하지 않고, 대용량 데이터 처리나 세밀한 모니터링 같은 부분에서는 더 개선할 여지가 있고요. 하지만 기존 시스템의 복잡성과 개발 비용 문제는 확실히 해결되었다고 생각합니다.
비슷한 고민을 하고 계신 분들께 조금이나마 도움이 되었으면 좋겠습니다. 특히 "기술 도입"보다는 "조직과 책임의 분리"라는 관점에서 접근해보시길 권해드립니다.
다음 편에서는 Sink Connector 설정과 더 구체적인 트러블슈팅 경험들을 다뤄보겠습니다.
'Develop > DATA Engineering' 카테고리의 다른 글
| GitOps로 Kafka Connect 커넥터를 체계적으로 관리하기 (0) | 2025.11.01 |
|---|---|
| [MSK+Kafka Connect] 실시간 CDC 파이프라인 튜닝 & DB 로그 메커니즘 (0) | 2025.09.30 |
| 대용량 데이터를 전송하는 방법(1) - Message Queue (0) | 2024.01.07 |
| [빅데이터를 지탱하는 기술] 01 - 1주차 스터디 (0) | 2022.09.14 |
| Airflow (0) | 2022.05.13 |