분산 데이터 처리 (Distributed Data Processing)

목차


왜 분산 데이터 처리가 필요한가

서비스가 커지면 데이터 변경이 하나의 데이터베이스 안에서 끝나지 않는 경우가 많습니다.

예를 들어:

  • 주문 생성 후 이벤트를 메시지 브로커에 발행
  • 결제 완료 후 검색 인덱스와 캐시를 갱신
  • 회원 정보 변경 후 다른 서비스로 동기화

이때 문제는 간단하지 않습니다.

  • DB에는 커밋됐는데 이벤트 발행이 실패할 수 있음
  • 이벤트는 두 번 발행됐는데 소비자는 한 번만 처리해야 할 수 있음
  • 여러 워커가 병렬로 처리하면서 순서가 꼬일 수 있음

즉, 분산 데이터 처리의 핵심은
데이터 변경과 외부 시스템 전파를 어떻게 일관성 있게 연결할지입니다.

트랜잭션 일반론은 트랜잭션 처리 및 일관성, 복구 관점은 백업과 복구 (Backup & Recovery) 문서와 같이 보면 좋습니다.


문제의 본질: DB 커밋과 외부 전파의 간극

가장 흔한 문제는 다음 두 작업을 하나처럼 다루고 싶다는 점입니다.

  1. 데이터베이스 트랜잭션 커밋
  2. 메시지 브로커나 다른 저장소로 변경 사항 전파

하지만 이 둘은 보통 서로 다른 시스템입니다.

예를 들어 주문 생성 코드가 다음 순서로 동작한다고 가정해 봅시다.

  1. orders 테이블에 주문 저장
  2. Kafka에 order-created 이벤트 발행

여기서 다음 실패가 가능합니다.

  • 주문 저장 성공, 이벤트 발행 실패
  • 이벤트 발행 성공, 주문 저장 rollback
  • 발행 재시도 중 중복 이벤트 생성

좋은 답변은 “트랜잭션으로 묶는다”에서 끝나지 않습니다.
같은 DB 안에서의 원자성과, 외부 시스템까지 포함한 전파 보장은 다르다고 먼저 구분하는 편이 좋습니다.

Kafka의 producer/consumer, offset, rebalance 같은 구현 상세는 Kafka (Apache Kafka) 문서가 더 직접적인 심화 자료입니다.


Outbox Pattern

Outbox Pattern 은 애플리케이션 데이터와 이벤트 발행 정보를 같은 데이터베이스 트랜잭션 안에 함께 기록하는 방식입니다.

대표 흐름은 다음과 같습니다.

  1. 주문 데이터를 저장한다
  2. outbox_events 같은 테이블에 발행할 이벤트를 함께 저장한다
  3. 별도 프로세스가 outbox를 읽어 메시지 브로커로 발행한다
  4. 발행 성공 후 상태를 갱신한다

이 방식의 장점은 DB 커밋과 이벤트 기록을 한 트랜잭션으로 묶을 수 있다는 점입니다.

  • 장점: 주문은 저장됐는데 이벤트가 아예 기록되지 않는 문제를 줄일 수 있음
  • 장점: 애플리케이션 레벨에서 구현하기 비교적 명확함
  • 단점: outbox 테이블 청소, 재시도, 발행 상태 관리가 필요함
  • 단점: “즉시 발행”이 아니라 비동기 전파로 감각이 바뀜

간단한 예시는 다음과 같습니다.

CREATE TABLE outbox_events (
  id BIGINT PRIMARY KEY,
  aggregate_type TEXT NOT NULL,
  aggregate_id BIGINT NOT NULL,
  event_type TEXT NOT NULL,
  payload JSONB NOT NULL,
  status TEXT NOT NULL,
  created_at TIMESTAMP NOT NULL
);

핵심은 이벤트를 “나중에 발행할 데이터”로 저장하는 것입니다.

면접에서는 Outbox를 설명할 때
DB 트랜잭션 안에서는 이벤트 기록까지만 보장하고, 실제 발행은 비동기로 분리한다고 말하는 편이 좋습니다.


CDC (Change Data Capture)

CDC(Change Data Capture)
데이터베이스의 변경 사항을 읽어 다른 시스템으로 전달하는 방식입니다.

Outbox가 애플리케이션 주도 패턴이라면, CDC는 보통 DB 변경 로그나 테이블 변경 자체를 읽는 방식으로 설명할 수 있습니다.

대표 활용은 다음과 같습니다.

  • 검색 인덱스 동기화
  • 데이터 웨어하우스 적재
  • 캐시/조회 모델 갱신
  • 다른 서비스로의 비동기 데이터 전파

여기서 데이터 웨어하우스, lake, 실시간 분석 저장소가 왜 필요한지는
OLTP와 OLAP (OLTP vs OLAP) 문서와 같이 보면 서비스 트랜잭션 시스템과 분석 시스템의 경계가 더 선명해집니다.

CDC의 장점과 주의점은 다음과 같습니다.

  • 장점: 애플리케이션 코드가 직접 발행을 몰라도 됨
  • 장점: DB 변경 사실을 기준으로 downstream 동기화를 만들 수 있음
  • 단점: 로그 보존, 스키마 변경, offset 관리 같은 운영 포인트가 많음
  • 단점: 원본 트랜잭션 문맥을 애플리케이션 수준만큼 풍부하게 담기 어렵기도 함

즉, CDC는 “자동 동기화”처럼 보이지만
실제로는 변경 로그를 어떻게 읽고, 어디까지 신뢰하며, 어떻게 재처리할지가 중요합니다.


Polling Publisher와 Log-Based CDC

분산 데이터 전파를 구현할 때 자주 비교하는 두 방식입니다.

방식 동작 장점 주의점
Polling Publisher 애플리케이션/워커가 outbox 테이블을 주기적으로 읽음 이해하기 쉽고 구현이 단순함 polling 지연, 중복 처리, 락/경합 관리 필요
Log-Based CDC WAL/binlog 같은 변경 로그를 읽음 원본 DB 변경과 더 가깝게 추적 가능 운영 복잡도와 도구 의존성이 큼

Polling Publisher

status = PENDING 인 outbox row를 주기적으로 읽어 발행합니다.

  • 장점: 애플리케이션 통제가 쉬움
  • 장점: 디버깅 경로가 단순함
  • 주의점: polling 간격 때문에 지연이 생길 수 있음
  • 주의점: 여러 워커가 동시에 읽을 때 중복 발행 방지 장치가 필요함

Log-Based CDC

DB의 WAL, binlog 같은 변경 로그를 읽어 downstream으로 흘립니다.

  • 장점: DB 변경 사실을 더 직접적으로 캡처 가능
  • 장점: 고처리량 전파 파이프라인에 잘 맞는 경우가 많음
  • 주의점: 운영 도구, offset, 스키마 변경 관리가 더 어려움

좋은 답변은 둘 중 하나를 절대화하지 않습니다.

  • 애플리케이션 이벤트와 비즈니스 문맥이 중요하면 Outbox + polling이 자연스러울 수 있음
  • 데이터 파이프라인이나 검색/분석 동기화는 log-based CDC가 더 잘 맞을 수 있음

멱등성과 중복 처리

분산 처리에서는 중복이 생길 수 있다고 가정하는 편이 안전합니다.

이유는 다음과 같습니다.

  • 발행 성공 응답을 못 받아 재시도할 수 있음
  • 소비자가 처리 후 ack 전에 죽을 수 있음
  • 네트워크 오류로 같은 메시지가 다시 전달될 수 있음

그래서 소비자 쪽에서는 멱등성(Idempotency) 이 매우 중요합니다.

멱등 처리는 보통 다음 방식으로 설명할 수 있습니다.

  • 이벤트 ID를 저장하고 이미 처리한 이벤트는 무시
  • unique constraint로 중복 삽입 방지
  • 상태 전이가 이미 완료된 요청은 재실행하지 않음
  • “한 번 더 실행해도 결과가 같게” 설계

실무에서는 구현 방식까지 한 단계 더 말할 수 있으면 답변이 훨씬 강해집니다.

Idempotency-Key, retry 위치, backoff 같은 일반 설계 원칙은 멱등성과 재시도 (Idempotency and Retry) 문서와 연결해 설명하면 좋습니다.

멱등성 구현 패턴

가장 흔한 방식은 처리 이력을 별도 테이블로 관리하는 방법입니다.

예를 들어 consumer가 이벤트를 처리할 때:

  1. event_idprocessed_events 같은 테이블에 기록 시도
  2. 이미 같은 event_id가 있으면 중복 처리로 보고 종료
  3. 처음 보는 이벤트만 실제 비즈니스 로직 수행
CREATE TABLE processed_events (
  event_id VARCHAR(100) PRIMARY KEY,
  processed_at TIMESTAMP NOT NULL
);

이 패턴의 핵심은 다음과 같습니다.

  • 중복 판별 기준이 명확함: event_id, idempotency_key, 외부 요청 ID
  • DB 제약을 활용할 수 있음: PK / UNIQUE로 중복 삽입 자체를 차단
  • 재처리에도 안전함: 같은 이벤트가 다시 와도 결과를 한 번만 반영

또 다른 방식은 비즈니스 엔티티 자체에 멱등 키를 저장하는 방법입니다.

  • 결제 요청 테이블에 idempotency_key를 unique로 둠
  • 주문 생성 요청에 외부 요청 ID를 기록
  • 이미 같은 키가 있으면 기존 결과를 그대로 반환

이때 중요한 점은 “중복 요청을 막는다”에서 끝나지 않고,
어디까지를 같은 요청으로 볼지 기준을 명확히 정하는 것입니다.

예를 들어 결제 완료 이벤트를 소비하는 서비스라면,

  • 이미 payment_id 가 처리된 적이 있으면 다시 반영하지 않음
  • 주문 상태가 이미 PAID 이면 중복 갱신을 무시

면접에서는 “메시지 브로커가 exactly-once를 보장한다”보다
애플리케이션도 중복에 안전하게 설계해야 한다고 설명하는 편이 좋습니다.


순서 보장과 재처리

분산 데이터 처리에서 또 자주 나오는 문제는 순서입니다.

예를 들어 다음 이벤트가 있다고 합시다.

  1. order-created
  2. order-paid
  3. order-cancelled

소비자가 이 순서를 기대하는데 실제 전달은 뒤섞일 수 있습니다.

그래서 다음 질문을 같이 봐야 합니다.

  • 같은 aggregate의 이벤트 순서를 보장해야 하는가
  • 순서가 조금 어긋나도 재시도로 수렴할 수 있는가
  • consumer가 out-of-order 이벤트를 임시 보관하거나 무시할 수 있는가

재처리(Reprocessing)도 중요합니다.

  • 버그 수정 후 과거 이벤트를 다시 돌리고 싶을 수 있음
  • 검색 인덱스를 전체 rebuild 해야 할 수 있음
  • CDC consumer를 특정 offset부터 다시 읽어야 할 수 있음

이때 중요한 포인트는 다음입니다.

  • 멱등성 없이는 재처리가 위험해짐
  • 이벤트 payload만으로 충분한가, 아니면 snapshot/재조회가 필요한가
  • 보존 기간과 offset 관리가 충분한가

즉, 순서와 재처리는 메시지 브로커 설정만의 문제가 아니라
이벤트 모델과 소비자 설계 문제이기도 합니다.


실패 시 복구 전략과 운영 포인트

분산 데이터 처리는 실패를 정상 경로의 일부로 봐야 합니다.

운영에서 자주 보는 포인트는 다음과 같습니다.

  • 재시도 큐: 일시 실패는 재시도하고, 영구 실패는 별도 격리
  • Dead Letter Queue: 계속 실패하는 메시지를 분리해 분석
  • offset/상태 모니터링: 소비 지연과 적체를 계속 확인
  • outbox 청소: 성공 이벤트를 무한정 쌓아 두지 않기
  • 스키마 진화: payload 구조 변경 시 소비자 호환성 관리
  • 관측성: trace id, event id, aggregate id 로 end-to-end 추적

복구 전략은 보통 다음 흐름으로 설명할 수 있습니다.

  1. 어디서 막혔는지 확인
  2. DB에 기록은 됐는지, 전파만 실패했는지 구분
  3. 재시도로 수습 가능한지 판단
  4. 필요하면 DLQ로 격리
  5. 멱등성을 전제로 재처리

좋은 답변은 “재시도합니다”보다
재시도, 격리, 재처리, 추적 가능성까지 운영 체계로 설명하는 것입니다.

복구 훈련과 운영 절차는 백업과 복구 (Backup & Recovery) 문서와 연결해서 보면 좋습니다.


면접 포인트

  • 분산 데이터 처리의 핵심은 DB 커밋과 외부 시스템 전파를 어떻게 연결할지입니다.
  • Outbox Pattern은 데이터 변경과 이벤트 기록을 같은 트랜잭션 안에 묶는 데 유용합니다.
  • CDC는 변경 로그를 활용한 전파 방식으로, 데이터 파이프라인과 동기화에 강점이 있습니다.
  • 중복은 생길 수 있다고 가정하고, 소비자를 멱등하게 설계하는 편이 안전합니다.
  • 좋은 답변은 순서 보장, 재처리, DLQ, offset 관리 같은 운영 포인트까지 함께 설명합니다.

참고 자료