> ## Documentation Index
> Fetch the complete documentation index at: https://0x38a.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 0x01. 데이터 파이프라인 개론 - Batch에서 Streaming까지

> 배치와 실시간 처리의 차이, 스트리밍 아키텍처의 핵심 개념, 그리고 Data Mesh와 Data Fabric까지 데이터 파이프라인의 전체 그림을 그려본다.

데이터는 생성되는 순간부터 가치를 잃기 시작한다.

어제의 주식 시세는 오늘의 투자 판단에 쓸 수 없고, 1시간 전 사기 거래 탐지는 이미 돈이 빠져나간 뒤다. 그렇다고 모든 데이터를 실시간으로 처리하는 것이 항상 정답은 아니다. 월간 매출 리포트를 밀리초 단위로 갱신할 필요는 없기 때문이다.

데이터 파이프라인의 핵심 질문은 결국 하나다. "**이 데이터를 얼마나 빨리 처리해야 하는가?**" 이 글에서는 배치 처리와 스트림 처리의 차이에서 출발하여, 이 둘을 조합하는 아키텍처 패턴, 그리고 조직 차원의 데이터 관리 패러다임인 Data Mesh와 Data Fabric까지 데이터 파이프라인의 전체 그림을 그려본다.

***

## Batch Processing: 모아서 한 번에

**배치 처리**(Batch Processing) 는 데이터를 일정 기간 동안 축적한 뒤 한꺼번에 처리하는 방식이다. 마치 우편물을 하루치 모아서 한 번에 분류하는 것과 같다.

전통적인 데이터 처리 방식이며, ETL(Extract, Transform, Load) 파이프라인의 근간이다. 매일 밤 전날의 거래 데이터를 집계하거나, 주 단위로 사용자 행동 패턴을 분석하는 것이 대표적인 예시다.

### 배치 처리의 특징

* **높은 처리량**(Throughput): 대량의 데이터를 효율적으로 한 번에 처리할 수 있다. 개별 레코드에 대한 오버헤드가 낮다.
* **높은 지연**(Latency): 데이터가 축적될 때까지 기다려야 하므로, 결과를 얻기까지 수 분에서 수 시간이 걸린다.
* **단순한 오류 처리**: 작업이 실패하면 전체를 다시 실행하면 된다. 입력 데이터가 고정되어 있기 때문에 **재현성**(Reproducibility) 이 높다.
* **자원 효율성**: 필요할 때만 대량의 컴퓨팅 자원을 할당하고, 작업이 끝나면 반환한다.

대표적인 배치 처리 프레임워크로는 **Apache Hadoop**의 MapReduce, **Apache Spark**의 배치 모드 등이 있다. Spark는 데이터를 메모리에 올려 처리하기 때문에 MapReduce보다 수십 배 빠르지만, 본질적으로 데이터를 모아서 처리한다는 점에서 배치 패러다임에 속한다.

### 배치 처리가 적합한 경우

* 일별/주별/월별 리포트 생성
* 대규모 데이터 마이그레이션
* 머신러닝 모델의 학습(Training)
* 이력(Historical) 데이터 분석

***

## Real-time Processing: 도착하는 즉시

**실시간 처리**(Real-time Processing) 는 데이터가 생성되는 즉시 처리하는 방식이다. 우편물 비유를 이어가면, 편지가 도착할 때마다 바로 분류하고 배달하는 것이다.

여기서 "실시간"이라는 용어에 대해 짚고 넘어갈 필요가 있다. 엄밀히 말하면 대부분의 데이터 시스템에서 말하는 실시간은 **준실시간**(Near Real-time) 이다. 밀리초에서 수 초 단위의 지연이 존재하며, 이는 항공기 제어 시스템 같은 **경성 실시간**(Hard Real-time) 과는 구분된다.

> 데이터 엔지니어링에서 "실시간 처리"는 일반적으로 이벤트 발생 후 수 밀리초\~수 초 이내에 처리 결과를 도출하는 것을 의미한다. 이를 **스트림 처리**(Stream Processing) 라고도 부른다.

### 실시간 처리의 특징

* **낮은 지연**(Low Latency): 이벤트 발생 후 거의 즉시 결과를 얻을 수 있다.
* **연속적 처리**: 데이터를 끊임없이 소비하고 처리한다. 시작과 끝이 명확한 배치와 달리, 스트림은 **무한한 데이터**(Unbounded Data) 를 다룬다.
* **복잡한 상태 관리**: 이벤트 간의 관계를 추적하려면 처리 중 **상태**(State) 를 유지해야 하며, 이것이 스트림 처리의 가장 어려운 부분이다.
* **정확성 보장의 어려움**: 네트워크 지연, 이벤트 순서 뒤바뀜, 중복 전달 등의 문제를 처리해야 한다.

대표적인 스트림 처리 프레임워크로는 **Apache Kafka Streams**, **Apache Flink**, **Apache Spark Structured Streaming** 등이 있다.

### 실시간 처리가 적합한 경우

* 실시간 사기 거래 탐지
* IoT 센서 데이터 모니터링
* 실시간 추천 시스템
* 주식 거래 시스템
* 실시간 대시보드

***

## Batch vs. Stream: 무엇을 선택할 것인가

둘 중 하나만 고르는 것이 아니다. 대부분의 현실 시스템은 배치와 스트림을 **함께** 사용한다. 핵심은 각각의 트레이드오프를 이해하고, 요구사항에 맞게 조합하는 것이다.

| 기준         | Batch Processing | Stream Processing                      |
| ---------- | ---------------- | -------------------------------------- |
| **지연**     | 높음 (분\~시간)       | 낮음 (밀리초\~초)                            |
| **처리량**    | 매우 높음            | 상대적으로 낮음                               |
| **데이터 범위** | 유한 (Bounded)     | 무한 (Unbounded)                         |
| **정확성**    | 높음 (재실행 용이)      | 보장 어려움 (at-least-once, exactly-once 등) |
| **복잡도**    | 상대적으로 낮음         | 높음 (상태 관리, 순서 보장)                      |
| **비용 패턴**  | 간헐적 고비용          | 지속적 저비용                                |

이 두 가지를 어떻게 조합할 것인가에 대한 대표적인 아키텍처 패턴이 바로 **Lambda 아키텍처**와 **Kappa 아키텍처**다.

### Lambda Architecture

**Lambda 아키텍처**는 Nathan Marz가 제안한 패턴으로, 배치 레이어와 스피드 레이어를 **병렬로** 운영한다.

```
                    ┌─────────────────┐
                    │   Batch Layer   │ ── 정확한 결과 (높은 지연)
                    │  (MapReduce,    │
  Data Source ──┬──>│   Spark 등)     │──┐
                │   └─────────────────┘  │   ┌──────────────┐
                │                        ├──>│ Serving Layer │──> Query
                │   ┌─────────────────┐  │   └──────────────┘
                └──>│   Speed Layer   │──┘
                    │  (Storm, Flink  │
                    │   등)           │ ── 빠른 근사치 (낮은 지연)
                    └─────────────────┘
```

* **Batch Layer**: 전체 데이터를 대상으로 정확한 결과를 계산한다. 시간이 걸리지만 신뢰할 수 있다.
* **Speed Layer**: 최근 데이터만 실시간으로 처리하여 빠른 근사치를 제공한다. Batch Layer의 결과가 준비되면 대체된다.
* **Serving Layer**: 두 레이어의 결과를 합쳐서 최종 뷰를 제공한다.

Lambda의 장점은 **정확성과 속도를 모두** 확보할 수 있다는 것이다. 하지만 치명적인 단점이 있다. 동일한 비즈니스 로직을 배치와 스트림 **두 곳에서 중복 구현**해야 한다는 점이다. 코드베이스가 두 벌이 되고, 유지보수 비용이 두 배로 증가한다.

### Kappa Architecture

**Kappa 아키텍처**는 Jay Kreps(LinkedIn, Apache Kafka 공동 창시자)가 Lambda의 복잡성을 해결하기 위해 제안했다. 핵심 아이디어는 단순하다. **모든 데이터를 스트림으로 처리한다.**

```
  Data Source ──> Event Log (Kafka) ──> Stream Processor ──> Serving Layer ──> Query
```

* 모든 데이터는 **이벤트 로그**(Event Log) 에 순서대로 기록된다.
* 스트림 프로세서가 이 로그를 소비하여 결과를 생성한다.
* 로직을 변경하거나 재처리가 필요하면, 로그의 **처음부터 다시 재생**(replay) 하면 된다.

배치 처리가 필요 없는 것이 아니라, 배치 처리를 **스트림의 특수한 경우**로 취급하는 것이다. Kafka 같은 이벤트 로그가 과거 데이터를 충분히 보관하고 있으면, 로그를 처음부터 재생하는 것이 곧 배치 처리가 된다.

Kappa는 **단일 코드베이스**로 배치와 스트림을 모두 처리할 수 있어 유지보수가 간결하다. 다만, 대량의 히스토리 데이터를 재처리할 때 스트림 프로세서의 성능이 충분하지 않을 수 있다는 한계가 있다.

> Lambda vs. Kappa는 "어느 쪽이 더 좋은가"의 문제가 아니다. 시스템의 데이터 볼륨, 지연 요구사항, 팀의 운영 역량에 따라 적합한 패턴이 달라진다. 최근에는 Apache Flink, Spark Structured Streaming 등이 배치와 스트림을 통합 처리하는 기능을 제공하면서, 두 아키텍처의 경계가 점차 흐려지고 있다.

***

## Streaming Architecture: 핵심 구성 요소

스트리밍 아키텍처를 제대로 이해하려면, 그 내부를 구성하는 핵심 개념들을 하나씩 살펴봐야 한다.

### Event Sourcing: 상태가 아닌 이벤트를 저장한다

전통적인 시스템은 **현재 상태**를 데이터베이스에 저장한다. 은행 계좌의 잔액이 50만 원이면, 데이터베이스에는 `balance = 500000`이 기록된다. 이전에 얼마였는지, 왜 바뀌었는지는 알 수 없다.

**이벤트 소싱**(Event Sourcing) 은 발상을 뒤집는다. 상태 대신 **상태를 변화시킨 이벤트들의 시퀀스**를 저장한다.

```
[이벤트 1] 계좌 개설 → 초기 잔액 0원
[이벤트 2] 입금 100만 원 → 잔액 100만 원
[이벤트 3] 출금 30만 원 → 잔액 70만 원
[이벤트 4] 이체 수신 20만 원 → 잔액 90만 원
```

현재 잔액은 이벤트들을 순서대로 재생(replay)하면 언제든 계산할 수 있다. 이 방식의 장점은 명확하다.

* **완전한 감사 추적**(Audit Trail): 모든 변경 이력이 남는다.
* **시간 여행 쿼리**: 특정 시점의 상태를 복원할 수 있다.
* **이벤트 재생**: 버그 수정 후 이벤트를 다시 처리하여 올바른 상태를 재계산할 수 있다.

반면, 이벤트가 계속 쌓이므로 **저장 공간**이 많이 필요하고, 현재 상태를 조회하려면 이벤트를 처음부터 재생해야 하므로 **읽기 성능**이 떨어질 수 있다. 이를 보완하기 위해 특정 시점의 상태를 **스냅샷**(Snapshot) 으로 저장해두는 기법을 함께 사용한다.

### Message Queue: 생산자와 소비자를 분리한다

**메시지 큐**(Message Queue) 는 데이터를 생산하는 쪽(Producer)과 소비하는 쪽(Consumer)을 **분리**(Decoupling) 하는 중간 버퍼다. 식당의 주문 전표 시스템과 비슷하다. 웨이터(Producer)가 전표를 꽂아두면, 주방(Consumer)이 자기 속도에 맞춰 전표를 가져가 요리한다. 웨이터는 주방이 바쁜지 아닌지 신경 쓸 필요가 없다.

메시지 큐의 두 가지 주요 모델이 있다.

**Point-to-Point**: 하나의 메시지를 하나의 Consumer만 소비한다. 작업 분배에 적합하다. 대표적으로 **RabbitMQ**, **Amazon SQS** 등이 있다.

**Publish-Subscribe**(Pub/Sub): 하나의 메시지를 여러 Consumer가 구독하여 소비한다. 이벤트 브로드캐스팅에 적합하다. 대표적으로 **Apache Kafka**, **Amazon SNS**, **Google Pub/Sub** 등이 있다.

특히 **Apache Kafka**는 단순한 메시지 큐를 넘어 **분산 이벤트 로그** 역할을 한다. 메시지를 소비한 뒤에도 설정된 보존 기간 동안 삭제하지 않기 때문에, 여러 Consumer가 각자의 속도로 같은 데이터를 독립적으로 소비할 수 있고, 필요하면 과거 시점부터 다시 읽을 수 있다. 이 특성이 Kappa 아키텍처를 가능하게 하는 핵심이다.

### Stream Processor: 흐르는 데이터를 가공한다

**스트림 프로세서**(Stream Processor) 는 메시지 큐에서 데이터를 받아 **실시간으로 변환, 집계, 분석**하는 엔진이다.

스트림 처리에서 가장 까다로운 개념 중 하나가 **윈도우**(Window) 다. 무한히 흐르는 데이터에서 "최근 5분간의 평균"이나 "지난 1시간 동안의 합계"를 계산하려면, 데이터를 시간 기준으로 잘라야 한다.

주요 윈도우 유형은 다음과 같다.

* **Tumbling Window**(텀블링 윈도우): 고정 크기의 겹치지 않는 구간. 예를 들어, 매 5분마다 집계.
* **Sliding Window**(슬라이딩 윈도우): 고정 크기지만 일정 간격으로 슬라이드하며 겹침이 발생. 예를 들어, 5분 윈도우가 1분마다 이동.
* **Session Window**(세션 윈도우): 이벤트 간 비활성 간격(gap)을 기준으로 동적으로 구간을 결정. 사용자 세션 분석에 유용.

```
Tumbling Window (5분):
|  0-5분  |  5-10분 | 10-15분 |  ...

Sliding Window (5분 크기, 1분 간격):
|  0-5분  |
  |  1-6분  |
    |  2-7분  |
      ...

Session Window (gap = 3분):
| 이벤트 A, B | ──3분 이상 공백── | 이벤트 C, D, E |
```

또 하나의 핵심 개념은 **이벤트 시간**(Event Time) 과 **처리 시간**(Processing Time) 의 구분이다. 이벤트 시간은 데이터가 실제로 발생한 시점이고, 처리 시간은 시스템에 도착하여 처리되는 시점이다. 네트워크 지연이나 시스템 장애로 둘 사이에 차이가 생기며, 이를 제대로 처리하지 않으면 집계 결과가 부정확해진다.

예를 들어, 오후 2:59에 발생한 결제 이벤트가 네트워크 지연으로 오후 3:01에 도착했다면, 이 이벤트는 "오후 2시-3시 매출"에 포함되어야 할까, "오후 3시-4시 매출"에 포함되어야 할까? Apache Flink 같은 프레임워크는 **워터마크**(Watermark) 라는 메커니즘을 사용하여 "이벤트 시간 기준으로 여기까지의 데이터는 모두 도착했다"고 판단하는 기준점을 제공한다.

대표적인 스트림 프로세서를 비교하면 다음과 같다.

| 프레임워크                                 | 처리 모델    | 특징                                |
| ------------------------------------- | -------- | --------------------------------- |
| **Apache Flink**                      | 네이티브 스트림 | 이벤트 시간 처리, exactly-once 보장, 낮은 지연 |
| **Apache Spark Structured Streaming** | 마이크로 배치  | Spark 생태계 통합, SQL 인터페이스           |
| **Apache Kafka Streams**              | 네이티브 스트림 | Kafka 내장, 별도 클러스터 불필요, 경량         |

***

## Data Mesh: 데이터도 도메인이 소유한다

지금까지 다룬 기술적 요소를 넘어, 조직 차원에서 데이터를 어떻게 관리할 것인가에 대한 패러다임도 변화하고 있다.

전통적인 데이터 아키텍처는 **중앙 집중형**이다. 하나의 데이터 팀이 전사의 데이터를 수집하고, 정제하고, 제공한다. 데이터 웨어하우스(Data Warehouse)든 데이터 레이크(Data Lake)든, 핵심은 **하나의 중앙 저장소에 모든 데이터를 모은다**는 것이다.

이 방식은 소규모 조직에서는 잘 작동하지만, 조직이 성장하면 병목이 발생한다.

* 중앙 데이터 팀이 모든 도메인의 비즈니스 로직을 이해해야 한다.
* 데이터 요청이 몰리면 중앙 팀이 **병목**(Bottleneck) 이 된다.
* 데이터 품질 문제의 책임 소재가 불명확해진다.
* 데이터 파이프라인이 거대한 **모놀리스**(Monolith) 가 되어 변경이 어려워진다.

**Data Mesh**는 Zhamak Dehghani가 2019년에 제안한 패러다임으로, 마이크로서비스가 모놀리식 애플리케이션을 분해한 것처럼, 중앙 집중형 데이터 아키텍처를 **분산형**으로 전환한다.

### Data Mesh의 4가지 원칙

**1. Domain Ownership**(도메인 소유권)

데이터의 소유권과 책임을 해당 데이터를 가장 잘 이해하는 **도메인 팀**에게 부여한다. 주문 데이터는 주문 팀이, 결제 데이터는 결제 팀이 소유하고 관리한다. 중앙 데이터 팀에게 "이 필드가 뭔가요?"라고 물어볼 필요가 없다.

**2. Data as a Product**(데이터를 제품처럼)

각 도메인이 제공하는 데이터를 **제품**(Product) 으로 취급한다. 제품에는 품질 기준이 있고, 사용자(소비자)가 있으며, SLA(Service Level Agreement)가 있다.

좋은 데이터 제품이 갖춰야 할 요건은 다음과 같다.

* **발견 가능**(Discoverable): 다른 팀이 어떤 데이터가 있는지 쉽게 찾을 수 있다.
* **주소 지정 가능**(Addressable): 표준화된 방식으로 접근할 수 있다.
* **신뢰 가능**(Trustworthy): 품질이 보장되고, SLA가 명시되어 있다.
* **자기 설명적**(Self-describing): 스키마, 의미, 변경 이력이 문서화되어 있다.
* **상호 운용 가능**(Interoperable): 다른 도메인의 데이터와 결합하여 사용할 수 있다.

**3. Self-serve Data Platform**(셀프 서비스 플랫폼)

각 도메인 팀이 데이터 제품을 쉽게 만들고 관리할 수 있도록 **플랫폼**을 제공한다. 모든 도메인 팀이 Kafka 클러스터를 직접 운영하거나 Spark 파이프라인을 밑바닥부터 구축할 필요는 없다. 플랫폼 팀이 공통 인프라, 도구, 템플릿을 제공하고, 도메인 팀은 이를 활용하여 비즈니스 로직에 집중한다.

**4. Federated Computational Governance**(연합 거버넌스)

분산이 혼돈을 의미하지는 않는다. 도메인 간 **상호 운용성**을 보장하기 위한 전사적 표준과 정책이 필요하다. 데이터 형식, 네이밍 규칙, 보안 정책, 품질 기준 등을 **자동화된 방식**으로 적용한다. 중앙에서 일일이 검토하는 것이 아니라, 정책을 코드로 정의하고 플랫폼에서 자동 적용하는 것이 핵심이다.

> Data Mesh는 기술이 아니라 **조직 원칙**이다. 특정 도구나 프레임워크가 아니라, 데이터에 대한 소유권, 책임, 거버넌스를 재설계하는 접근 방식이다.

***

## Data Fabric: 메타데이터로 연결한다

**Data Fabric**은 Data Mesh와 자주 비교되지만, 접근 방식이 근본적으로 다르다. Data Mesh가 **조직 구조의 변화**를 통해 문제를 해결한다면, Data Fabric은 **기술 중심의 통합 레이어**를 통해 문제를 해결한다.

Data Fabric의 핵심은 **메타데이터**(Metadata) 의 활용이다. 조직 전체에 흩어져 있는 데이터의 위치, 형식, 관계, 품질, 사용 패턴 등의 메타데이터를 수집하고 분석하여, **데이터 통합과 관리를 자동화**한다.

### Data Fabric의 핵심 구성 요소

* **Knowledge Graph**: 데이터 자산 간의 관계를 그래프 형태로 모델링한다. "이 테이블은 저 API의 데이터를 기반으로 만들어졌다" 같은 **데이터 리니지**(Data Lineage) 를 추적한다.
* **Active Metadata Management**: 메타데이터를 단순히 기록하는 것을 넘어, 실시간으로 분석하여 데이터 파이프라인을 **자동 최적화**한다. 예를 들어, 자주 함께 조회되는 데이터셋을 자동으로 사전 조인(pre-join)하거나, 품질 이상을 자동으로 감지한다.
* **Unified Data Access**: 데이터가 온프레미스에 있든, 클라우드에 있든, 어떤 포맷이든 상관없이 **단일 인터페이스**로 접근할 수 있게 한다.

### Data Mesh vs. Data Fabric

| 기준         | Data Mesh            | Data Fabric          |
| ---------- | -------------------- | -------------------- |
| **접근 방식**  | 조직적 (탈중앙화)           | 기술적 (자동화)            |
| **핵심 원동력** | 도메인 소유권              | 메타데이터 활용             |
| **데이터 관리** | 도메인 팀이 분산 관리         | 기술 레이어가 통합 관리        |
| **거버넌스**   | 연합형 (도메인 자율 + 전사 표준) | 중앙형 (자동화된 정책 적용)     |
| **조직 변화**  | 큼 (소유권 재배분 필요)       | 작음 (기존 구조 위에 레이어 추가) |
| **자동화 수준** | 플랫폼 의존               | 높음 (ML 기반 메타데이터 분석)  |

두 패러다임은 상호 배타적이지 않다. Data Mesh의 조직 원칙을 따르면서, 각 도메인의 데이터를 Data Fabric의 기술 레이어로 연결하는 **하이브리드 접근**도 가능하다. 실제로 많은 조직이 이 두 가지를 결합하여 사용하고 있다.

***

## 정리

데이터 파이프라인은 단일 기술이 아니라, 여러 계층의 의사결정이 쌓인 아키텍처다. 이 글에서 다룬 핵심 개념을 정리하면 다음과 같다.

* **Batch Processing**: 데이터를 모아서 한 번에 처리한다. 높은 처리량과 정확성이 강점이지만, 지연이 크다.
* **Stream Processing**: 데이터를 도착 즉시 처리한다. 낮은 지연이 강점이지만, 상태 관리와 정확성 보장이 어렵다.
* **Lambda / Kappa Architecture**: 배치와 스트림을 조합하는 패턴이다. Lambda는 두 레이어를 병렬로 운영하고, Kappa는 스트림 하나로 통합한다.
* **Streaming Architecture**: 이벤트 소싱, 메시지 큐, 스트림 프로세서가 핵심 구성 요소이며, 윈도우와 이벤트 시간 처리가 핵심 난제다.
* **Data Mesh**: 도메인 중심의 분산 데이터 소유권을 통해 중앙 집중형의 병목을 해소한다.
* **Data Fabric**: 메타데이터 기반의 기술 레이어로 데이터 통합과 관리를 자동화한다.

"어떤 기술을 쓸까?"보다 중요한 질문은 "**이 데이터의 가치는 시간에 얼마나 민감한가?**" 다. 그 답에 따라 배치, 스트림, 또는 둘의 조합이 결정되고, 조직의 규모와 성숙도에 따라 Data Mesh나 Data Fabric 같은 관리 패러다임이 선택된다.
