CDC 파이프라인을 Debezium과 Flink로 재설계한 이유

실시간 CDC 파이프라인 재설계, 신뢰성과 정합성을 위해 무엇을 고려할 것인가
Jess Jang's avatar
Feb 23, 2026
CDC 파이프라인을 Debezium과 Flink로 재설계한 이유

우리는 초기에는 Apache NiFi를 사용해 MySQL binlog 기반 CDC 파이프라인을 빠르게 구축했습니다. PoC 단계와 초기 요구사항을 충족시키기에는 충분히 효과적인 선택이었습니다.

이후 변경 이벤트의 규모가 점차 커지면서 기존 구조는 확장성과 병렬 처리 측면에서 한계를 드러내기 시작했습니다. 동시에 CDC 데이터가 단순한 데이터 적재를 넘어 비즈니스 모니터링에 활용되기 시작하면서, 파이프라인에 요구되는 안정성과 신뢰성의 기준 또한 함께 높아졌습니다.

이 글에서는 NiFi 기반 CDC 파이프라인을 Debezium과 Apache Flink로 재설계하게 된 배경과, 그 과정에서 고려했던 기술적·운영적 판단을 정리합니다.

1. 왜 CDC가 중요한가

What is CDC(Change Data Capture)?

CDC는 데이터베이스에서 발생하는 INSERT, UPDATE, DELETE 등의 상태 변화를 식별하여 외부 시스템으로 실시간 전달하는 기술입니다. 대부분의 RDBMS는 데이터 복제(Replication)나 장애 복구를 위해 모든 트랜잭션 이력을 로그 파일(MySQL binlog, PostgreSQL WAL 등)에 순차적으로 기록하는데, CDC는 이 변경 로그를 직접 활용합니다.

CDC(Change Data Capture) 동작 원리

이러한 방식은 운영 중인 DB에 별도의 쿼리 부하를 주지 않으면서도, 삭제(Delete) 이벤트나 짧은 간격으로 발생하는 연속적인 변경 이력을 누락 없이 정확하게 포착할 수 있다는 장점이 있습니다. 즉, CDC는 데이터베이스를 단순한 '저장소'가 아닌 '지속적인 이벤트의 원천'으로 바라볼 수 있게 해주는 기술적 토대입니다.

CDC는 어디에 활용될 수 있는가

위와 같이 운영 DB에 영향을 주지 않으면서도 데이터 변화를 정교하게 캡처할 수 있다는 점 때문에, CDC는 단순 적재를 넘어 시스템 간의 상태를 동기화하는 핵심 인터페이스로 활용됩니다.

  • 저장소 간 데이터 동기화: 소스 DB에 부하를 주지 않고 분석용 저장소(DW, Data Lake)나 검색 엔진(Elasticsearch)의 데이터를 최신 상태로 유지합니다. 가장 보편적이고 활용 비중이 높은 사례입니다.

  • 실시간 또는 준실시간 비즈니스 모니터링: 주문 상태 변경이나 결제 완료 등 주요 비즈니스 지표의 변화를 실시간으로 추적하여 대시보드에 반영하거나 이상 징후를 탐지합니다.

  • 이벤트 기반 아키텍처(EDA) 구현: 서비스 간 직접적인 DB 조회 대신 변경 이벤트를 전달함으로써 서비스 간 결합도를 낮추고, 분산된 환경에서도 데이터 정합성을 유지합니다.

  • 데이터 정합성 검증 및 감사(Audit): 데이터의 최종 상태뿐만 아니라 변경 이력 전체를 보유함으로써, 특정 시점의 데이터 복구나 비즈니스 정합성 검증의 근거로 활용합니다.

신뢰성 있는 파이프라인의 필요성

CDC 데이터가 단순한 참고용 지표를 넘어 위와 같이 비즈니스 로직의 핵심 데이터 소스로 활용되기 시작하면, 파이프라인에 요구되는 기술적 기준은 훨씬 엄격해집니다.

  • 순서 보장(Ordering): 변경 이벤트의 발생 순서가 뒤바뀌면 데이터 최종 상태의 무결성이 깨지게 됩니다.

  • 정확히 한 번 처리(Exactly-once): 네트워크 순회나 시스템 재시작 상황에서도 데이터의 중복이나 누락이 발생하지 않아야 합니다.

  • 스키마 대응(Schema Evolution): 소스 DB의 스키마 변경이 전체 파이프라인의 중단으로 이어지지 않도록 유연하게 대응해야 합니다.

결국 CDC 파이프라인을 설계할 때 가장 핵심적인 고민은 어떻게 변경 데이터를 소스 DB와 동일한 상태로 신뢰성 있게 전달할 것인가에 집중됩니다. 이러한 맥락에서, 기존 파이프라인의 설계와 운영 방식을 다시 한번 점검할 필요가 생겼습니다.

2. 기존 CDC 파이프라인의 한계

저희는 초기 단계에서 Apache NiFi를 사용해 MySQL binlog 기반의 CDC 파이프라인을 구축했습니다. 이전 포스트인 Nifi에서 Apache Flink로, 실시간 SMS 파이프라인 개선기에서도 다루었듯, NiFi는 GUI 기반의 시각적인 데이터 플로우를 제공하여 파이프라인의 전체 구조를 빠르게 구성하고 배포할 수 있다는 장점이 있었습니다. 하지만 서비스 규모가 확장됨에 따라 NiFi의 운영 방식은 다음과 같은 구조적 한계에 부딪혔습니다.

기존 CDC 파이프라인

확장성 및 처리 안정성 문제

  • 확장성의 제한: NiFi는 데이터 흐름을 직관적으로 보여주지만, 특정 프로세서에 부하가 집중될 때 이를 유연하게 수평 확장(Scale-out)하기 어렵습니다. DB측의 벌크 작업과 같이 이벤트 폭증 시 특정 노드에 자원 점유율이 쏠리며 서버가 다운되는 현상이 반복되었고, 이 과정에서 CDC 데이터의 누락과 오프셋(Offset) 유실이 발생했습니다.

    이벤트 폭증에 따른 CPU 리소스 고갈 및 서버 다운
  • 오프셋 관리의 불투명함: 장애 발생 시 어디까지 로그를 읽었는지에 대한 상태(State) 관리가 투명하지 않아, 복구 과정에서 데이터 중복이나 유실 지점을 파악하기 위해 상당한 공수를 투입해야 했습니다.

관리 및 인프라 운영 측면의 제약

  • 코드 기반 관리의 부재: NiFi의 GUI 중심 설정은 Git 등을 통한 버전 관리와 코드 리뷰를 어렵게 만듭니다. 이는 파이프라인의 변경 이력을 추적하거나 CI/CD를 통한 자동화된 배포 프로세스를 구축하는 데 큰 제약이 되었습니다.

  • 인프라 격리의 어려움: NiFi는 대개 하나의 클러스터 내에서 여러 데이터 플로우가 자원을 공유하는 구조를 가집니다. 특정 파이프라인에서 과부하가 발생하면 클러스터 전체의 안정성에 영향을 주며, 이는 곧 다른 파이프라인의 지연이나 중단으로 이어지는 'Noisy Neighbor' 문제를 야기했습니다.

비즈니스 모니터링 팀의 새로운 요구사항

결정적인 변화는 CDC 데이터를 실시간 비즈니스 모니터링의 핵심 데이터 소스로 활용하려는 내부 팀의 요구사항이었습니다. 데이터를 소비하는 사용자 관점에서 다음과 같은 구체적인 기술적 기준이 제시되었습니다.

  1. 특정 시점의 데이터 복제 완결성 검증 (Point-in-time Verification): 단순히 데이터를 전달하는 것을 넘어, 특정 시점까지 발생한 원본 DB의 모든 변경 사항이 누락 없이 타겟에 반영되었음을 시스템적으로 확인하고 보장할 수 있어야 한다는 요구였습니다. 이는 모니터링 결과에 대한 비즈니스적 신뢰도를 확보하기 위한 필수 전제 조건이었습니다.

  2. 데이터 포맷의 일관성 및 생산성: 데이터 분석 및 활용의 생산성을 위해, 기존 데이터 플랫폼에 배치 기반으로 적재된 데이터(Full Load Table)와 동일한 Enum 타입 및 시간 형식을 유지해야 했습니다.

기존의 NiFi 파이프라인은 데이터를 전달(Delivery)하는 데 최적화되어 있었기에, 위와 같이 엄격한 데이터 완결성 검증배치 데이터와의 정교한 포맷 일치를 대규모 스트림에서 수행하기에는 구조적인 한계가 명확했습니다. 저희는 이러한 요구사항을 충족하기 위해 새로운 스트림 처리 엔진을 검토하게 되었습니다.

3. 기술적 해결책 도출: 새로운 파이프라인의 기준 정의

새로운 CDC 아키텍처를 설계하며 저희가 가장 중요하게 고려한 가치는 파이프라인의 신뢰성과 운영 안정성이었습니다. 비즈니스 모니터링 팀의 요구사항인 '데이터 완결성 검증'과 '배치 데이터와의 정합성' 역시, 결국 파이프라인이 생성하는 데이터를 얼마나 신뢰할 수 있느냐의 문제와 직결되었기 때문입니다.

이를 위해 저희는 변경 데이터를 추출하는 인제스트 도구(Ingestion Tool)로서 Debezium과 AWS DMS를, 데이터를 처리하는 스트림 엔진(Stream Engine)으로서 Apache Flink를 포함한 여러 대안을 다음과 같은 우선순위에 따라 검토했습니다.

3.1. 인제스트 도구 검토: 안정적인 데이터 캡처를 위한 선택 (Debezium vs AWS DMS)

원천 DB의 변경 로그를 캡처할 도구로 매니지드 서비스인 AWS DMS와 오픈소스인 Debezium을 비교 검토했습니다.

운영 편의성 면에서는 인프라 관리 부담이 적고 오토스케일링(Autoscaling)을 지원하는 AWS DMS가 유리했습니다. 하지만 저희가 목표로 하는 '신뢰성 있는 실시간 모니터링 파이프라인'의 기술적 요구사항을 충족하기에는 다음과 같은 결정적인 차이가 있었습니다.

  • 실시간 스트리밍과 데이터 처리 효율성: AWS DMS는 데이터베이스 간 복제(Replication)에 초점이 맞춰져 있어, 필터링이나 포맷 변환을 적용하려면 별도의 ETL 단계가 필요했습니다. 반면 Debezium은 Kafka Connect 기반으로 동작하며, SMT(Transform) 등을 통해 추출 단계에서 기본적인 필터링과 형식 변환을 적용할 수 있어 파이프라인 복잡도를 줄일 수 있었습니다.

  • 검증 가능성 및 무결성 (Point-in-time Verification): 모니터링 데이터의 신뢰성을 확보하기 위해서는 '복제가 얼마나 지연되고 있는지'와 '어느 지점까지 복제가 완료되었는지'를 투명하게 파악할 수 있어야 했습니다. Debezium은 MilliSecondsBehindSource(DB와 커넥터 간의 지연 지표)와 같은 메트릭을 실시간으로 제공하여 복제 현황을 정확히 모니터링할 수 있게 해줍니다. 또한, 복제 오프셋(Offset) 상태를 Kafka 내부 토픽을 통해 투명하게 관리하므로 파이프라인 전반의 가시성이 높습니다. 이러한 지표와 관리 방식은 장애 발생 시에도 특정 시점까지의 데이터 완결성을 시스템적으로 검증할 수 있는 강력한 근거가 됩니다.

  • 스키마 동기화의 신뢰성: Debezium은 Kafka와 연동하여 스키마 변경 이력을 메시지 메타데이터에 포함해 자동 전송합니다. 별도의 수동 파이프라인 구축 없이도 스키마 진화(Schema Evolution)에 안정적으로 대응할 수 있다는 점은 운영상 큰 이점입니다.

  • 다양한 환경 지원 및 안정성: 저희 환경은 RDS뿐만 아니라 EC2 기반의 MySQL도 혼재되어 있었습니다. DMS는 EC2 기반 MySQL 지원에 제약이 있었으나, Debezium은 어떤 소스 환경에서든 일관된 동작을 보장했습니다. 이는 인프라 환경 변화에도 파이프라인의 안정성을 유지하는 핵심 요소가 되었습니다.

  • 보안 및 격리 환경 대응: 서비스 계정과 데이터 플랫폼 계정이 분리된 환경에서 컬럼 단위 암호화 처리가 필요했습니다. Debezium은 이러한 보안 요구사항을 충족하면서도 안정적으로 데이터를 전송할 수 있는 세밀한 설정 기능을 제공했습니다.

항목

Debezium

AWS DMS

실시간 스트리밍

이벤트 기반 모니터링 최적화

데이터 복제 및 준실시간 처리 중심

데이터 처리 효율

필터링/변환 기능을 통한 복잡도 감소

별도 ETL 도구 필수

검증 가능성

지연 메트릭 및 오프셋 관리의 투명성

관리형 서비스로 인한 내부 가시성 제한

MySQL 환경

RDS/EC2 기반 모두 안정적 지원

EC2 기반 MySQL 작동 제약 존재

민감 데이터 보안

컬럼 단위 암호화 및 계정 분리 적합

암호화 지원 부족 및 구성 제약

3.2. 스트림 엔진 검토: 왜 다시 Apache Flink인가

Debezium이 캡처한 변경 이벤트를 가공하고 비즈니스 로직을 태우기 위한 엔진으로 Kafka Connect, Spark Structured Streaming, Apache Flink를 검토했습니다.

  • Kafka Connect (SMT): 가벼운 변환(Single Message Transforms)은 가능하지만, 배치 스냅샷 데이터와 타입을 맞추기 위한 복잡한 Enum 변환이나 타임존 처리 등 고도의 비즈니스 로직을 수행하기에는 확장성과 유연성이 부족했습니다. 기본적으로 데이터의 단순 이동(Ingest)에 최적화된 도구이기 때문입니다.

  • Spark Structured Streaming: Exactly-once를 지원하며 배치 처리에 강점이 있지만, 근본적으로 마이크로 배치(Micro-batch) 방식으로 동작합니다. 이는 초 단위 이하의 낮은 지연 시간(Low Latency)이 필수적인 실시간 비즈니스 모니터링 환경에서 응답 속도의 불확실성을 야기할 우려가 있었습니다.

결국 저희는 다음과 같은 이유로 Apache Flink를 최종 선택했습니다.

1. 검증된 운영 편의성 및 인프라 격리

이미 SMS 파이프라인 재설계를 통해 Flink의 Job 단위 자원 격리 능력을 확인했습니다. 각 CDC Job이 독립된 자원을 할당받아 실행되므로, 특정 테이블의 데이터 폭증이 다른 파이프라인에 영향을 주지 않는 인프라 안정성을 확보할 수 있었습니다. 또한, 사내에 이미 구축된 Flink 모니터링 및 배포 체계를 그대로 활용할 수 있다는 점은 운영 효율성 측면에서 압도적인 장점이었습니다.

2. 네이티브 스트리밍과 강력한 상태 관리

Flink는 마이크로 배치가 아닌 네이티브 스트리밍 방식으로 동작하여 매우 낮은 지연 시간을 보장합니다. 특히 RocksDB 기반의 상태 백엔드(State Backend)는 대량의 이전 데이터 상태를 유지하며 복잡한 타입 변환 로직을 수행하기에 가장 적합한 구조를 제공합니다.

3. 체크포인트를 통한 Exactly-once 보장

Flink의 체크포인트(Checkpoint) 메커니즘은 3.1에서 언급한 Debezium의 투명한 오프셋 정보와 결합하여, 장애 발생 시에도 소스부터 싱크까지 정확히 한 번 처리(Exactly-once)를 완벽하게 보장합니다. 이는 "특정 시점의 복제 완결성 검증"이라는 비즈니스 요구사항을 기술적으로 해결하는 핵심 열쇠가 되었습니다.

의사결정 요약: 신뢰할 수 있는 기반 위에 세운 요구사항

결국 저희가 Debezium + Apache Flink 조합을 최종 선택한 이유는, 단순히 기능이 많아서가 아니라 가장 제어하기 쉽고, 어떤 환경에서도 장애 복구 메커니즘이 명확하며, 다른 시스템과 격리되어 안정적으로 동작한다는 신뢰가 있었기 때문입니다. 이러한 기술적 기반이 확보되었기에 비즈니스 팀이 요구한 고수준의 검증 로직과 정합성 확보를 안정적으로 구현할 수 있었습니다.

4. 최종 아키텍처: Debezium과 Flink의 유기적 결합

재설계된 CDC 파이프라인은 원천 DB의 변경 사항을 표준화된 이벤트로 캡처(Debezium)하고, 이를 비즈니스 규격에 맞게 정밀 가공(Flink)하여 적재하는 구조를 가집니다. 각 컴포넌트는 다음과 같은 구체적인 기술적 역할을 수행합니다.

Debezium과 Flink를 활용한 CDC 파이프라인

4.1. Debezium

Debezium은 원천 데이터베이스의 물리적인 변경(binlog)을 후속 시스템이 즉시 처리할 수 있는 표준화된 이벤트 스트림으로 변환하는 역할을 담당합니다.

  • 메시지 내 스키마 정보 내장 (Self-describing Metadata): value.converter.schemas.enable 설정을 통해 모든 변경 메시지에 데이터 구조 정보를 함께 포함합니다. 이 설계의 핵심은 별도의 스키마 레지스트리나 외부 메타데이터 관리 시스템을 구축하지 않고도 스키마 변경에 즉각 대응하는 것입니다. 메시지 자체가 자신의 구조를 설명하고 있으므로, 컬럼 추가나 타입 변경 등 소스 DB의 변화가 발생하더라도 Flink 단에서 추가적인 시스템 연동 없이 유입된 메시지만으로 변화를 인지하고 유연하게 처리할 수 있습니다.

    {
      "schema": {
        "type": "struct",
        "name": "dbz.fin_core_db.loan.Envelope",
        "fields": [
          {
            "type": "struct",
            "field": "before",
            "optional": true,
            "name": "dbz.fin_core_db.loan.Value",
            "fields": [
              { "type": "int64", "optional": false, "field": "loan_id" },
              { "type": "string", "optional": false, "field": "status" },
              { "type": "double", "optional": true, "field": "amount" },
              {
                "type": "string",
                "optional": false,
                "name": "io.debezium.time.ZonedTimestamp",
                "version": 1,
                "field": "updated_at"
              }
            ]
          },
          { "type": "struct", "field": "after", "optional": true, ... }, // before와 동일한 구조
          { "type": "struct", "field": "source", "optional": false, ... }, // binlog 메타데이터
          { "type": "string", "field": "op", "optional": false }          // 작업 유형 (u, c, d 등)
        ]
      },
      "payload": {
        "before": {
          "loan_id": 20260101,
          "status": "APPROVED",
          "updated_at": "2026-01-01T11:30:00Z"
        },
        "after": {
          "loan_id": 20260101,
          "status": "DISBURSED",
          "amount": 5000000.0,
          "disbursed_at": "2026-01-01T12:00:00Z",
          "updated_at": "2026-01-01T12:00:00Z"
        },
        "source": {
          "db": "fin_core_db",
          "table": "loan",
          "file": "mysql-bin.128573",
          "pos": 79483662
        },
        "op": "u",
        "ts_ms": 1767268800000
      }
    }
  • 추출 단계의 세밀한 데이터 핸들링: 데이터 플랫폼의 기존 규격과 실시간 스트림 데이터를 일치시키기 위해 추출 단계에서 일부 타입 변환 규칙을 적용해 downstream에서의 처리 부담을 줄였습니다.

4.2. Apache Kafka

카프카는 단순히 데이터를 전달하는 통로를 넘어, 수많은 소스 테이블의 복잡도를 제어하고 하부 엔진의 처리 성능을 결정짓는 전략적 완충 지대 역할을 합니다.

  • 시스템 간 결합도 분리 및 완충: 인제스트(Debezium)와 프로세싱(Flink) 단계를 물리적으로 분리하여 시스템 간 의존성을 낮춥니다. 이를 통해 엔진의 업데이트나 일시적인 장애 상황에서도 데이터 유실 없이 변경 이력을 안전하게 보관하며, 각 단계가 독립적으로 최적의 속도로 동작할 수 있는 완충 환경을 제공합니다.

  • 운영 효율을 위한 통합 토픽 설계 (Topic Consolidation): 서비스별로 수백 개의 테이블이 존재하는 환경(예: 대출 서비스의 130개 이상의 테이블)에서 발생하는 토픽 관리의 복잡도를 낮추기 위해 DB 단위의 통합 토픽을 설계했습니다. Debezium에서 RegexRouter를 통해 여러 테이블 이벤트를 논리적으로 통합하되, 필요 시 라우팅 규칙만 수정하여 특정 테이블을 물리적으로 즉시 격리할 수 있는 유연한 구조를 취했습니다.

4.3. Apache Flink

Apache Flink는 Debezium으로부터 전달받은 로우 이벤트를 신뢰할 수 있는 데이터로 재구성하고, 장애 상황에서도 무결성을 유지하는 프로세서 역할을 수행합니다.

  • Before/After 상태 보존과 메타데이터 추가: Debezium이 전송하는 이벤트에서 after(변경 후) 상태는 최상위로 평탄화(Flatten)하여 일반 테이블 구조처럼 즉시 활용 가능하게 하고, before(변경 전) 상태는 중첩 객체(Nested Object)로 유지하여 변경 전후를 비교할 수 있도록 설계했습니다. 여기에 binlog_position과 binlog_timestamp 같은 메타데이터를 추가함으로써 특정 시점까지의 복제 완결성을 검증하고, INSERT/UPDATE/DELETE 유형에 따른 분기 처리를 가능케 했습니다. 이러한 구조는 정합성 검증이나 감사(Audit) 요구사항을 충족할 뿐만 아니라, 변경 데이터 한 로우 자체가 비즈니스 로직을 트리거하는 독립적인 '이벤트'로 활용될 수 있는 기반이 됩니다.

    {
      /* [Metadata] 정합성 검증 및 순서 보장용 */
      "binlog_type": "UPDATE",
      "binlog_timestamp": 1767268800000,
      "binlog_filename_position": 12857379483662,
      "binlog_database": "fin_core_db",
      "binlog_table": "loan",
    
      /* [After] 현재의 최신 상태 - 평탄화되어 즉시 쿼리 및 분석 가능 */
      "loan_id": 20260101,
      "status": "DISBURSED",     // 현재 상태: 대출 실행 완료
      "amount": 5000000.0,
      "disbursed_at": "2026-01-01 12:00:00",
      "updated_at": "2026-01-01 12:00:00",
    
      /* [Before] 변경 전 상태 - 중첩 객체로 보존되어 상태 전환 분석 가능 */
      "before": {
        "loan_id": 20260101,
        "status": "APPROVED",     // 이전 상태: 승인됨
        "amount": null,
        "disbursed_at": null,
        "updated_at": "2026-01-01 11:30:00"
      },
    
      /* [Partition] 스키마 변경 시 물리적 경로 분리 */
      "dt": "2026-01-01",
      "schema_hash": "ffbac683"
    }
  • 체크포인트를 통한 Exactly-once 보장: Flink는 5분(300,000ms) 간격으로 체크포인트를 생성하여 '정확히 한 번 처리'를 보장합니다. 체크포인트는 현재 처리 중인 Kafka 오프셋과 Flink 내부 상태(State)를 스냅샷으로 저장하여, 장애 발생 시 정확한 지점부터 재처리할 수 있게 합니다. 이는 소스부터 싱크까지 데이터 유실이나 중복 없이 원천 DB의 상태를 타겟 시스템에 완벽하게 재현하는 핵심 메커니즘입니다.

5. 실전 기록: 안정성과 성능을 위한 시행착오

설계도대로 파이프라인을 구축하는 과정에서 마주한 기술적 난제들은 단순히 도구를 교체하는 것 이상의 구체적인 고민을 요구했습니다. 이론적인 설계와 실제 운영 환경의 간극을 좁히기 위해 처리 성능, 데이터 무결성, 운영 편의성이라는 세 가지 관점에서 최적의 균형점을 찾아야 했습니다. 

특히 불균형한 데이터 트래픽에서의 리소스 최적화, Parquet 포맷의 물리적 제약을 극복한 스키마 대응, 그리고 기존 파이프라인의 알림 체계를 넘어 즉각적인 장애 판단과 대응이 가능한 모니터링까지, 실무 과정에서 겪은 네 가지 핵심 도전 과제와 그 해결 과정을 공유합니다.

5.1. 서비스별 파이프라인 분리: 스케일링 불균형과 결함 격리

초기에는 관리 효율을 위해 여러 서비스 DB(플랫폼, 대출, 정산 등)의 CDC를 하나의 대규모 Flink Job에서 처리했습니다. 하지만 실제 운영 데이터의 흐름은 예상보다 훨씬 불균형했습니다.

  • 문제 상황: 모든 파이프라인이 동일한 Parallelism으로 스케일링되면서, 데이터량이 적은 서비스 오버프로비저닝(Over-provisioning) 상태가 되고, 이때 상대적으로 리소스가 남기 때문에 전체 지표 기반의 오토스케일링이 작동하지 않아 데이터량이 많은 타 서비스들은 언더프로비저닝(Under-provisioning) 상태에 빠지는 불균형이 발생했습니다.

  • 해결: 이를 해결하기 위해 각 서비스 DB별로 Flink Job을 완전히 분산시켰습니다. 각 파이프라인이 자신의 부하에만 정직하게 반응하며 개별적으로 스케일링(Scale-out)할 수 있게 되었고, 특정 서비스의 장애가 다른 파이프라인으로 전파되지 않는 결함 격리(Fault Isolation)를 실현했습니다.

5.2. 스키마 변경 대응: 예외 발생에서 구조적 격리로

운영 중인 DB의 스키마 변경은 피할 수 없는 상수입니다. 하지만 Parquet 포맷은 동일 파일 내에서 스키마가 일관되어야 한다는 물리적 제약이 있어, 스트리밍 도중 스키마가 변하면 쓰기 오류가 발생합니다.

  • 초기 시도(RuntimeException을 통한 강제 롤링의 한계): 처음에는 DynamicParquetWriterFactory 내에서 스키마 변경을 감지하면 즉시 flush()와 finish()를 호출한 뒤 RuntimeException을 던져 잡(Job)을 실패하게 만들었습니다. 재시작 시 새로운 체크포인트 이후 새 파일이 생성되도록 유도한 전략이었으나, 실제로는 flush()가 의도대로 동작하지 않은 채 잡만 실패하게 되었습니다. 결과적으로 데이터는 안전하게 기록되지 않았고, 빈번한 잡 중단과 장애 알림으로 인해 운영 부담이 가중되었습니다.

  • 여러 Writer 관리 시도와 구조적 제약: 이를 해결하기 위해 하나의 서브태스크 내에서 여러 writer를 운용하는 방식을 검토했으나, Flink의 StreamingFileSink 구조상 하나의 FSDataOutputStream은 하나의 파일만을 점유하므로 물리적인 파일 분리가 불가능했습니다. 또한 동일 버킷 내에서 writer만 교체하는 방식 역시 실제 파일 롤링으로 이어지지 않는 구조적 한계가 있었습니다.

  • 최종 해결(BinlogBucketAssigner를 통한 경로 분리): 파일을 소프트웨어적으로 억지로 쪼개는 대신, 스키마가 다르면 저장 경로(Bucket) 자체를 다르게 설정하는 방식을 택했습니다. BinlogBucketAssigner가 스키마의 해시값을 경로에 포함하도록 수정하여 물리적인 격리를 구현했습니다.

  • 결과: Flink는 버킷 ID가 달라지면 자동으로 새로운 writer를 열기 때문에, 스키마 변경 시 자연스럽게 파일이 분리됩니다. schema_hash 파티션 전략을 통해 스키마 충돌 문제를 근본적으로 방지했으며, 이제 DynamicParquetWriterFactory에서 무거운 예외 처리를 수행할 필요 없이 중단 없는 안정적인 적재가 가능해졌습니다.

5.3. Flink Throughput 최적화

시스템을 구축하고 초기 데이터 적재를 테스트하던 중, 예상보다 훨씬 낮은 처리량(Throughput) 문제에 직면했습니다.

  • 문제 상황: 전체 시스템 리소스를 스케일 업(Scale-up)했음에도 데이터 처리 속도는 제자리걸음이었습니다. 모니터링 결과, Flink Job 내의 수많은 서브태스크 중 단 하나만 100% 가동되고 나머지는 유휴 상태(Idle)인 것을 확인했습니다. 원인은 카프카 토픽이 단일 파티션으로 구성되어 있어, 컨슈머인 Flink가 아무리 스케일 아웃을 해도 단 하나의 소스 태스크만 작동할 수밖에 없는 구조적 한계 때문이었습니다.

    단일 파티션에서 데이터를 소비하는 Flink
  • 해결 - 파티션 확장 및 소스 병렬도 고정: 성능의 결정론적 예측이 가능하도록 모든 CDC 토픽의 파티션을 6개로 확장했습니다. 이와 동시에 Flink의 스트림 읽는 쪽(KafkaSource) 병렬성을 파티션 수와 동일하게 6으로 고정했습니다. 소스 태스크가 파티션을 1:1로 전담 소비하게 함으로써 인입 단계의 네트워크 셔플링(Shuffling)을 차단하고 초기 처리량을 극대화했습니다.

  • 결과 - 유연한 후속 스케일링: 소스 단 이후에는 rebalance를 적용하여 후속 프로세싱 단계들이 데이터 인입량에 따라 독립적으로 병렬성을 조절할 수 있도록 구성했습니다. 이를 통해 입구에서의 병목을 제거하는 동시에, 실제 연산 및 적재 단계에서는 부하에 맞춰 유연하게 리소스를 활용할 수 있는 구조를 완성했으며, 결과적으로 초기 파이프라인 대비 약 4배 이상의 처리량(Throughput) 향상을 달성했습니다.

5.4. 실시간 가시성 확보: 메트릭 기반의 모니터링과 알림 체계

새로운 파이프라인의 운영 안정성을 위해 저희는 Flink 메트릭을 Grafana 대시보드로 시각화하여 실시간 가시성을 확보했습니다. 

Grafana 대시보드

특히 데이터 유실이나 지연을 방지하기 위해 recordsLagsMax와 bytesConsumedRate를 핵심 지표로 관리하며, 이상 징후 발생 시 즉시 Slack 알림이 발송되도록 설정했습니다.

  • recordsLagsMax (컨슈머 렉): 카프카 토픽의 파티션 중 가장 많이 뒤처진 레코드 수를 나타냅니다.  이 수치가 상승한다는 것은 소스 DB의 변경 속도를 Flink가 따라가지 못하고 있다는 강력한 신호입니다. 저희는 이 지표에 임계값을 설정하여, 실시간성이 훼손될 우려가 있을 때 즉시 개입하거나 병렬도를 조정할 수 있도록 알림을 구축했습니다.

  • bytesConsumedRate (소비 처리량): 초당 카프카에서 읽어오는 데이터의 크기(Bytes)를 측정합니다. 이 지표는 특히 데이터 유입 중단을 즉각 탐지하는 데 매우 유용합니다.  원천 DB나 카프카 커넥터에 문제가 생겨 데이터가 들어오지 않으면 수치가 즉시 0으로 떨어지기 때문에, 장애 상황을 실시간으로 인지할 수 있습니다.

6. 도입 후 변화: 신뢰를 넘어 비즈니스 가치로

새로운 CDC 파이프라인 도입은 단순히 데이터를 옮기는 기술적 성공을 넘어, 조직이 데이터를 신뢰하고 활용하는 방식을 근본적으로 변화시켰습니다. 무엇보다 수백 개의 테이블 변화에 일일이 대응하던 수동 운영 체제에서 벗어나 장애에 자율적으로 대응하는 운영 안정성을 확보했으며, 실시간 데이터와 배치 스냅샷의 규격을 일치시킴으로써 데이터 소스에 상관없이 동일한 분석 로직을 사용할 수 있는 일관된 분석 생산성을 제공하게 되었습니다. 이러한 견고한 신뢰를 바탕으로, 현재 CDC 데이터는 단순 적재를 넘어 비즈니스 핵심 영역에서 3가지 방식으로 가치를 창출하고 있습니다.

6.1. 데이터 품질 모니터링: 시스템 간 정합성 실시간 감지

과거에는 배치 작업이 끝난 후에야 발견할 수 있었던 시스템 간 상태 불일치를 이제는 실시간으로 포착합니다. 23개 이상의 정합성 체크 모델이 CDC 스트림 위에서 작동하며 복잡한 금융 데이터의 무결성을 감시합니다.

  • 상태 동기화 검증: 플랫폼, 대출, 정산 등 여러 시스템 간의 대출 상태가 일치하는지 실시간으로 대조합니다.

  • 이상 징후 즉시 탐지: 잔액이 0원임에도 'ACTIVE' 상태인 대출, 과납입(Overpaid) 발생 케이스, 결제 게이트웨이에서 멈춰버린(Stuck) 트랜잭션 등을 즉각 탐지하여 운영 리스크를 최소화합니다.

6.2. 비즈니스 메트릭 실시간 추적: 데이터 기반의 즉각적 의사결정

주요 비즈니스 퍼널 지표를 5분 간격으로 집계하여, 과거의 '사후 분석'을 '실시간 대응'으로 전환했습니다.

  • 대출 퍼널 모니터링: 신청(Applied)부터 실행(Disbursed)에 이르는 전 과정을 촘촘하게 추적하여 단계별 전환율(Conversion Rate)과 처리 시간(Time-to-Decision)을 실시간으로 계산합니다.

  • 성과 최적화: 파트너사별 성과를 실시간으로 모니터링하여, 지표 변동에 따라 즉각적인 비즈니스 액션을 취할 수 있는 환경을 구축했습니다.

6.3. CDC 기반 실시간 데이터 마트: 자산 재계산 및 검증

CDC 데이터의 변경 이력을 활용하여, 우리 서비스의 가장 핵심 지표인 자산 규모를 실시간으로 산출하는 'CDC 마트'를 구축했습니다.

  • 정밀한 자산 추적: 원금의 변동뿐만 아니라 상환, 상각, 면제액 등 복잡한 자금 흐름의 변경분을 실시간으로 추적하여 AUM을 재계산합니다.

  • 복층 검증 체계: 이렇게 계산된 실시간 마트 데이터는 기존의 배치 스냅샷 데이터와 상시 비교됩니다. 이를 통해 데이터 플랫폼이 제공하는 지표에 대한 신뢰도를 기술적으로 증명하고 있습니다.

7. 마치며: 데이터 플랫폼의 새로운 심장

이번 프로젝트의 성과는 단순히 노후화된 아키텍처를 세련되게 개선한 것에 그치지 않습니다. 기존에도 CDC 파이프라인은 존재했지만, 잦은 장애와 낮은 신뢰도로 인해 Production 레벨의 비즈니스 로직에 직접 활용하기에는 한계가 명확했습니다.

이번 재설계는 이 파이프라인을 실제 서비스 운영에 즉시 투입 가능한 Production 레벨로 끌어올렸다는 데 큰 의의가 있습니다. 이를 통해 데이터 플랫폼 내에서 서비스 데이터를 '1일 단위의 배치'가 아닌 '실시간' 레벨로 다룰 수 있게 되었으며, 결과적으로 조직 전체가 데이터에 기반한 의사결정을 훨씬 더 빠른 호흡으로 내릴 수 있는 기술적 토대를 마련했습니다.

현재는 시스템 정합성 검증과 비즈니스 모니터링 영역에서 그 가치를 증명하고 있지만, 1장에서 언급한 바와 같이 CDC의 잠재력은 여기서 끝이 아닙니다. 우리는 이번에 구축한 견고한 기반 위에서, 서비스 간 결합도를 낮추는 이벤트 기반 아키텍처(EDA)와 전사적 데이터의 신뢰를 담보하는 감사(Audit) 영역으로 그 범위를 끊임없이 확장해 나갈 것입니다.

신뢰할 수 있는 실시간 데이터 파이프라인은 이제 서비스의 안정성을 넘어, 비즈니스의 민첩성을 극대화하는 데이터 플랫폼의 핵심 심장부로 작동하고 있습니다.

다음 글 예고

안정적인 파이프라인을 구축했다면, 이제 이 데이터를 어떻게 효율적으로 저장하고 서비스할 것인가의 문제가 남습니다. 다음 글에서는 ‘CDC가 데이터 플랫폼을 바꾸는 방식: CDC Replication’을 주제로, 적재 단에서의 혁신을 다뤄보겠습니다.

Share article