- Published on
Data Mesh 아키텍처 완전 가이드 2025: 도메인 중심 데이터, 데이터 제품, 셀프서비스 플랫폼
- Authors

- Name
- Youngju Kim
- @fjvbn20031
들어가며: 왜 Data Mesh인가
조직이 성장하면 데이터도 폭발적으로 증가합니다. 그런데 대부분의 기업은 여전히 중앙 데이터 팀이 모든 데이터를 수집하고, 변환하고, 제공하는 구조를 유지하고 있습니다. 이 구조가 만들어내는 병목은 심각합니다.
중앙집중 데이터 아키텍처의 한계
[주문 도메인] ──ETL──┐
[결제 도메인] ──ETL──┤
[물류 도메인] ──ETL──┼──▶ [중앙 데이터 팀] ──▶ [데이터 웨어하우스]
[마케팅 도메인] ──ETL──┤ ↑ 병목!
[고객 도메인] ──ETL──┘
중앙 데이터 팀이 겪는 전형적인 문제들입니다.
- 도메인 지식 부족: 주문 데이터의 비즈니스 의미를 데이터 엔지니어가 완벽히 이해하기 어렵습니다
- 파이프라인 큐 대기: 새 데이터 요청이 백로그에 쌓여 몇 주씩 대기합니다
- 단일 장애점: 중앙 팀의 리소스가 전체 조직의 데이터 역량을 결정합니다
- 소유권 불명확: 데이터 품질 문제가 발생하면 도메인 팀과 데이터 팀 사이에서 책임이 불분명합니다
Zhamak Dehghani는 2019년 ThoughtWorks 블로그에서 이 문제에 대한 패러다임 전환을 제안했습니다. 바로 Data Mesh입니다.
Data Mesh의 핵심 아이디어
Data Mesh는 소프트웨어 엔지니어링의 마이크로서비스 아키텍처와 도메인 주도 설계(DDD) 원칙을 데이터 아키텍처에 적용한 것입니다.
[주문 도메인 팀] ──소유──▶ [주문 데이터 제품]
[결제 도메인 팀] ──소유──▶ [결제 데이터 제품]
[물류 도메인 팀] ──소유──▶ [물류 데이터 제품]
[마케팅 도메인 팀] ──소유──▶ [마케팅 데이터 제품]
↕ 셀프서비스 플랫폼 + 연합 거버넌스 ↕
1. Data Mesh의 4가지 원칙
Data Mesh는 4가지 핵심 원칙으로 구성됩니다. 이 원칙들은 독립적이 아니라 상호 보완적입니다.
원칙 1: 도메인 소유권 (Domain Ownership)
데이터의 소유권을 비즈니스 도메인 팀에게 위임합니다. 각 도메인 팀은 자신이 생성하는 데이터에 대해 end-to-end 책임을 집니다.
# 도메인 소유권 매핑 예시
domains:
order:
owner_team: "order-squad"
data_products:
- name: "orders-fact"
description: "주문 이벤트 기반 팩트 테이블"
- name: "order-items-dimension"
description: "주문 항목 디멘션"
responsibilities:
- "데이터 품질 보장"
- "SLA 준수"
- "스키마 변경 관리"
- "소비자 지원"
payment:
owner_team: "payment-squad"
data_products:
- name: "transactions-fact"
description: "결제 트랜잭션 팩트 테이블"
- name: "payment-methods-dimension"
description: "결제 수단 디멘션"
도메인 분류 기준:
| 도메인 유형 | 설명 | 예시 |
|---|---|---|
| 소스 정렬(Source-aligned) | 비즈니스 사실을 생성하는 도메인 | 주문, 결제, 재고 |
| 집합체 정렬(Aggregate-aligned) | 여러 소스를 결합하는 도메인 | 고객 360, 상품 카탈로그 |
| 소비자 정렬(Consumer-aligned) | 특정 소비 패턴에 최적화된 도메인 | 마케팅 분석, 재무 리포팅 |
원칙 2: 데이터를 제품으로 (Data as a Product)
데이터를 단순한 부산물이 아닌 **제품(Product)**으로 취급합니다. 데이터 제품에는 API처럼 명확한 인터페이스, 문서, SLA가 있어야 합니다.
# 데이터 제품 명세서 예시 (dataproduct.yaml)
apiVersion: datamesh/v1
kind: DataProduct
metadata:
name: orders-fact
domain: order
owner: order-squad
description: "실시간 주문 이벤트 기반 팩트 데이터"
spec:
# 검색 가능성 (Discoverability)
tags: ["order", "ecommerce", "fact-table"]
documentation:
url: "https://data-catalog.internal/orders-fact"
schema_doc: "https://schema-registry.internal/orders-fact/latest"
# 주소 지정 가능성 (Addressability)
output_ports:
- name: batch
type: bigquery
location: "project.dataset.orders_fact"
format: parquet
- name: streaming
type: kafka
location: "orders.fact.v2"
format: avro
- name: api
type: rest
location: "https://data-api.internal/orders-fact"
# 신뢰성 (Trustworthiness)
sla:
freshness: "5 minutes"
availability: "99.9%"
completeness: "99.5%"
quality_checks:
- type: "not_null"
columns: ["order_id", "customer_id", "created_at"]
- type: "unique"
columns: ["order_id"]
- type: "range"
column: "total_amount"
min: 0
# 자기 기술적 (Self-describing)
schema:
format: avro
registry: "https://schema-registry.internal"
subject: "orders-fact-value"
compatibility: BACKWARD
# 상호 운용성 (Interoperable)
global_standards:
id_format: "UUID v4"
timestamp_format: "ISO 8601 UTC"
currency_format: "ISO 4217"
# 보안 (Secure)
access_control:
classification: "confidential"
pii_columns: ["customer_email", "shipping_address"]
encryption: "AES-256"
원칙 3: 셀프서비스 데이터 플랫폼 (Self-Serve Data Platform)
도메인 팀이 데이터 인프라 전문가가 아니더라도 데이터 제품을 쉽게 구축하고 운영할 수 있도록 플랫폼을 제공합니다.
┌─────────────────────────────────────────────────────────┐
│ 셀프서비스 데이터 플랫폼 │
├─────────────────────────────────────────────────────────┤
│ [데이터 제품 빌더] [파이프라인 템플릿] [모니터링 대시보드] │
│ [스키마 레지스트리] [데이터 카탈로그] [접근 관리 포탈] │
├─────────────────────────────────────────────────────────┤
│ [인프라 추상화 계층] │
│ - Kubernetes Operators │
│ - Terraform Modules │
│ - CI/CD 파이프라인 │
├─────────────────────────────────────────────────────────┤
│ [기반 인프라] │
│ - Kafka / Flink / Spark │
│ - BigQuery / Snowflake / Databricks │
│ - Airflow / dbt │
└─────────────────────────────────────────────────────────┘
셀프서비스 플랫폼이 제공해야 할 핵심 기능입니다.
platform_capabilities:
data_product_lifecycle:
- "데이터 제품 생성 (scaffold/template)"
- "스키마 등록 및 버전 관리"
- "빌드/테스트/배포 자동화"
- "데이터 품질 모니터링"
infrastructure:
- "스토리지 프로비저닝 (S3, GCS, BigQuery)"
- "스트리밍 인프라 (Kafka 토픽, Flink 잡)"
- "배치 처리 (Spark, dbt)"
- "오케스트레이션 (Airflow DAG)"
governance_automation:
- "접근 권한 자동 프로비저닝"
- "데이터 분류 자동 태깅"
- "SLA 모니터링 및 알림"
- "데이터 리니지 자동 추적"
developer_experience:
- "CLI 도구 (datamesh-cli)"
- "웹 포탈 (데이터 카탈로그)"
- "SDK (Python, Java, Go)"
- "문서 자동 생성"
원칙 4: 연합 거버넌스 (Federated Computational Governance)
중앙화된 표준과 도메인의 자율성 사이에서 균형을 잡습니다. 핵심은 거버넌스를 **코드로 자동화(Computational)**하는 것입니다.
# 연합 거버넌스 구조
governance_model = {
"global_policies": {
# 중앙에서 정의 - 모든 도메인이 준수
"interoperability": {
"id_standard": "UUID v4",
"timestamp_standard": "ISO 8601 UTC",
"encoding": "UTF-8",
},
"security": {
"pii_encryption": "required",
"access_logging": "required",
"data_classification": ["public", "internal", "confidential", "restricted"],
},
"quality": {
"minimum_sla_availability": 0.99,
"schema_compatibility": "BACKWARD",
"documentation": "required",
},
},
"domain_autonomy": {
# 각 도메인이 자율적으로 결정
"technology_choice": "도메인 팀이 적합한 기술 선택",
"internal_modeling": "도메인 내부 데이터 모델링",
"release_cadence": "데이터 제품 릴리즈 주기",
"team_structure": "팀 내부 역할 및 프로세스",
},
"computational_enforcement": {
# 정책을 코드로 자동 검증
"ci_cd_gates": "빌드 시 정책 준수 자동 검증",
"runtime_monitoring": "운영 중 SLA 자동 모니터링",
"automated_classification": "PII 자동 탐지 및 태깅",
},
}
2. Data Mesh vs 기존 아키텍처 비교
아키텍처 비교표
| 특성 | Data Warehouse | Data Lake | Data Lakehouse | Data Fabric | Data Mesh |
|---|---|---|---|---|---|
| 아키텍처 방식 | 중앙집중 | 중앙집중 | 중앙집중 | 중앙집중 (자동화) | 분산 |
| 데이터 소유권 | 데이터 팀 | 데이터 팀 | 데이터 팀 | 데이터 팀 | 도메인 팀 |
| 거버넌스 | 중앙 | 느슨 | 중앙 | 자동화된 중앙 | 연합 |
| 주요 기술 | SQL, ETL | Hadoop, Spark | Delta, Iceberg | Knowledge Graph, AI | 다양 (도메인별) |
| 스케일링 단위 | 인프라 | 인프라 | 인프라 | 인프라 | 조직 (팀) |
| 적합한 규모 | 소-중 | 중-대 | 중-대 | 대 | 대 (다중 도메인) |
Data Fabric vs Data Mesh
Data Fabric과 Data Mesh는 종종 혼동되지만 근본적으로 다른 접근 방식입니다.
Data Fabric (기술 중심 접근)
================================
- 자동화된 메타데이터 관리
- AI/ML 기반 데이터 통합
- 중앙에서 관리하는 가상 데이터 레이어
- Knowledge Graph로 데이터 연결
- 기존 중앙 팀 구조 유지
Data Mesh (조직 중심 접근)
================================
- 도메인 기반 분산 소유권
- 데이터를 제품으로 취급
- 셀프서비스 플랫폼
- 연합 거버넌스
- 조직 구조 변화 필요
두 접근법은 상호 배타적이 아닙니다. Data Mesh의 셀프서비스 플랫폼 내부에 Data Fabric의 기술을 활용할 수 있습니다.
3. Data Product 설계 심화
데이터 제품의 6가지 특성
Zhamak Dehghani가 정의한 데이터 제품의 6가지 필수 특성입니다.
┌────────────────────────────────────────────────┐
│ Data Product 특성 │
├────────────────────────────────────────────────┤
│ │
│ 1. Discoverable (검색 가능) │
│ - 데이터 카탈로그에 자동 등록 │
│ - 의미 있는 메타데이터 │
│ │
│ 2. Addressable (주소 지정 가능) │
│ - 고유 URI/ARN │
│ - 버전 관리된 엔드포인트 │
│ │
│ 3. Trustworthy (신뢰할 수 있는) │
│ - SLA 보장 │
│ - 품질 메트릭 공개 │
│ │
│ 4. Self-describing (자기 기술적) │
│ - 스키마 + 문서 + 샘플 데이터 │
│ - 의미론적 메타데이터 │
│ │
│ 5. Interoperable (상호 운용 가능) │
│ - 글로벌 표준 준수 │
│ - 표준화된 식별자/타임스탬프 │
│ │
│ 6. Secure (안전한) │
│ - 세밀한 접근 제어 │
│ - PII 보호 │
│ │
└────────────────────────────────────────────────┘
데이터 제품 인터페이스: Output Port
데이터 제품은 다양한 소비 패턴을 지원하기 위해 여러 Output Port를 제공합니다.
# 다양한 Output Port 예시
class OrdersDataProduct:
"""주문 데이터 제품 - 다중 Output Port"""
def __init__(self):
self.domain = "order"
self.name = "orders-fact"
self.version = "2.1.0"
# Batch Output Port (분석가/데이터 사이언티스트용)
@output_port(type="batch")
def bigquery_table(self):
return {
"location": "analytics.order_domain.orders_fact_v2",
"format": "columnar",
"partitioned_by": "order_date",
"clustered_by": ["customer_id", "status"],
"refresh": "hourly",
}
# Streaming Output Port (실시간 시스템용)
@output_port(type="streaming")
def kafka_topic(self):
return {
"location": "order.fact.v2",
"format": "avro",
"schema_registry": "https://schema-registry.internal",
"partitioned_by": "customer_id",
"retention": "7 days",
}
# API Output Port (애플리케이션용)
@output_port(type="api")
def rest_api(self):
return {
"base_url": "https://data-api.internal/v2/orders",
"auth": "OAuth2",
"rate_limit": "1000 req/min",
"pagination": "cursor-based",
}
# File Output Port (ML 학습용)
@output_port(type="file")
def s3_export(self):
return {
"location": "s3://data-products/order/orders-fact/",
"format": "parquet",
"partitioned_by": ["year", "month", "day"],
"snapshot": "daily",
}
Input Port와 변환 로직
데이터 제품은 소스 데이터를 수신하는 Input Port와 비즈니스 로직에 따른 변환을 포함합니다.
-- dbt 모델 예시: 주문 팩트 테이블 변환
-- models/orders_fact.sql
WITH raw_orders AS (
-- Input Port: 운영 DB CDC 스트림
SELECT * FROM {{ source('order_cdc', 'orders') }}
),
raw_items AS (
SELECT * FROM {{ source('order_cdc', 'order_items') }}
),
enriched AS (
SELECT
o.order_id,
o.customer_id,
o.status,
o.created_at,
o.updated_at,
COUNT(i.item_id) AS item_count,
SUM(i.quantity * i.unit_price) AS subtotal,
SUM(i.discount_amount) AS total_discount,
SUM(i.quantity * i.unit_price) - SUM(i.discount_amount) AS total_amount,
o.currency
FROM raw_orders o
LEFT JOIN raw_items i ON o.order_id = i.order_id
GROUP BY o.order_id, o.customer_id, o.status,
o.created_at, o.updated_at, o.currency
),
with_sla_metrics AS (
SELECT
*,
-- 데이터 품질 메트릭
CASE
WHEN order_id IS NOT NULL
AND customer_id IS NOT NULL
AND total_amount >= 0
THEN TRUE ELSE FALSE
END AS is_valid,
CURRENT_TIMESTAMP() AS processed_at
FROM enriched
)
SELECT * FROM with_sla_metrics
4. 도메인 분해 전략
비즈니스 도메인에서 데이터 도메인으로
DDD의 바운디드 컨텍스트를 데이터 도메인 경계에 활용합니다.
전자상거래 비즈니스 도메인 분해
============================================
┌─────────────────────────────────────────┐
│ 소스 정렬 도메인 │
│ │
│ [주문] ──▶ orders-fact │
│ order-items-dim │
│ │
│ [결제] ──▶ transactions-fact │
│ refunds-fact │
│ │
│ [재고] ──▶ inventory-snapshot │
│ stock-movements-fact │
│ │
│ [고객] ──▶ customer-profiles │
│ customer-events-fact │
│ │
│ [상품] ──▶ product-catalog │
│ pricing-history │
└────────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 집합체 정렬 도메인 │
│ │
│ [Customer 360] ──▶ unified-customer │
│ (고객 + 주문 + 결제 통합) │
│ │
│ [Product Intelligence] ──▶ product-360 │
│ (상품 + 재고 + 가격 통합) │
└────────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 소비자 정렬 도메인 │
│ │
│ [마케팅 분석] ──▶ campaign-performance │
│ customer-segments │
│ │
│ [재무 리포팅] ──▶ revenue-report │
│ cost-analysis │
└─────────────────────────────────────────┘
도메인 경계 결정 기준
domain_boundary_criteria:
business_alignment:
- "조직 구조와 일치하는가?"
- "하나의 팀이 소유 가능한 범위인가?"
- "비즈니스 용어와 컨텍스트가 일관적인가?"
data_characteristics:
- "데이터의 변경 빈도가 유사한가?"
- "같은 소스 시스템에서 오는가?"
- "함께 쿼리되는 데이터인가?"
team_capacity:
- "팀이 데이터 제품을 유지할 역량이 있는가?"
- "도메인 전문가가 팀에 있는가?"
- "2-pizza 팀 규모로 관리 가능한가?"
5. 셀프서비스 인프라 구축
데이터 파이프라인 템플릿
도메인 팀이 즉시 사용할 수 있는 파이프라인 템플릿을 제공합니다.
# Cookiecutter 템플릿: data-product-template
# cookiecutter.json
template_config:
project_name: "{{ '{{' }} cookiecutter.project_name {{ '}}' }}"
domain: "{{ '{{' }} cookiecutter.domain {{ '}}' }}"
output_ports:
batch: true
streaming: false
api: false
# 생성되는 프로젝트 구조
project_structure:
- dataproduct.yaml # 데이터 제품 명세
- schema/
- v1.avsc # Avro 스키마
- transforms/
- models/
- staging/ # 스테이징 모델
- marts/ # 마트 모델
- dbt_project.yml
- quality/
- expectations.yaml # Great Expectations 설정
- tests/
- test_transforms.py
- test_quality.py
- ci/
- pipeline.yaml # CI/CD 파이프라인
- docs/
- README.md
- CHANGELOG.md
스키마 레지스트리
# 스키마 레지스트리 활용 예시
from confluent_kafka.schema_registry import SchemaRegistryClient
from confluent_kafka.schema_registry.avro import AvroSerializer
schema_registry_config = {
"url": "https://schema-registry.internal",
"basic.auth.user.info": "api-key:api-secret",
}
client = SchemaRegistryClient(schema_registry_config)
# 스키마 등록
order_schema = """
{
"type": "record",
"name": "OrderFact",
"namespace": "com.company.order",
"fields": [
{"name": "order_id", "type": "string", "doc": "고유 주문 ID (UUID v4)"},
{"name": "customer_id", "type": "string"},
{"name": "status", "type": {
"type": "enum",
"name": "OrderStatus",
"symbols": ["CREATED", "CONFIRMED", "SHIPPED", "DELIVERED", "CANCELLED"]
}},
{"name": "total_amount", "type": {"type": "bytes", "logicalType": "decimal", "precision": 10, "scale": 2}},
{"name": "currency", "type": "string", "doc": "ISO 4217 통화 코드"},
{"name": "created_at", "type": {"type": "long", "logicalType": "timestamp-millis"}},
{"name": "metadata", "type": ["null", {"type": "map", "values": "string"}], "default": null}
]
}
"""
# 호환성 검증 (BACKWARD)
schema_id = client.register_schema(
subject="orders-fact-value",
schema=Schema(order_schema, schema_type="AVRO"),
)
데이터 카탈로그
# DataHub를 활용한 데이터 카탈로그 등록
from datahub.emitter.rest_emitter import DatahubRestEmitter
from datahub.metadata.schema_classes import (
DatasetPropertiesClass,
OwnershipClass,
OwnerClass,
OwnershipTypeClass,
)
emitter = DatahubRestEmitter("https://datahub.internal")
# 데이터 제품 메타데이터 등록
dataset_properties = DatasetPropertiesClass(
name="orders-fact",
description="실시간 주문 이벤트 기반 팩트 데이터 제품",
customProperties={
"domain": "order",
"data_product_version": "2.1.0",
"sla_freshness": "5 minutes",
"sla_availability": "99.9%",
"classification": "confidential",
},
)
ownership = OwnershipClass(
owners=[
OwnerClass(
owner="urn:li:corpGroup:order-squad",
type=OwnershipTypeClass.DATAOWNER,
)
]
)
# 데이터 리니지 등록
lineage = UpstreamLineageClass(
upstreams=[
UpstreamClass(
dataset="urn:li:dataset:(urn:li:dataPlatform:mysql,order_service.orders,PROD)",
type=DatasetLineageTypeClass.TRANSFORMED,
)
]
)
데이터 품질 모니터링
# Great Expectations를 활용한 품질 체크
import great_expectations as gx
context = gx.get_context()
# 데이터 제품 품질 기대치 정의
validator = context.sources.pandas_default.read_dataframe(orders_df)
# 필수 컬럼 NULL 체크
validator.expect_column_values_to_not_be_null("order_id")
validator.expect_column_values_to_not_be_null("customer_id")
validator.expect_column_values_to_not_be_null("created_at")
# 유니크 체크
validator.expect_column_values_to_be_unique("order_id")
# 범위 체크
validator.expect_column_values_to_be_between(
"total_amount", min_value=0, max_value=1000000
)
# 참조 무결성
validator.expect_column_values_to_be_in_set(
"status",
["CREATED", "CONFIRMED", "SHIPPED", "DELIVERED", "CANCELLED"]
)
# 신선도 체크
validator.expect_column_max_to_be_between(
"created_at",
min_value=datetime.now() - timedelta(minutes=10),
max_value=datetime.now(),
)
results = validator.validate()
print(f"Quality Score: {results.statistics['success_percent']}%")
6. 연합 거버넌스 구현
데이터 계약 (Data Contract)
데이터 제품의 생산자와 소비자 간의 계약을 명시적으로 정의합니다.
# data-contract.yaml
dataContractSpecification: "0.9.3"
id: "urn:datacontract:order:orders-fact"
info:
title: "Orders Fact Data Contract"
version: "2.1.0"
owner: "order-squad"
contact:
name: "Order Squad"
email: "order-squad@company.com"
slack: "#order-data"
servers:
production:
type: BigQuery
project: analytics-prod
dataset: order_domain
terms:
usage: "Internal analytics and reporting"
limitations: "PII data must not be exported to external systems"
billing: "Cost allocated to consumer team"
models:
orders_fact:
description: "주문 팩트 테이블"
type: table
fields:
order_id:
type: string
required: true
unique: true
description: "고유 주문 식별자 (UUID v4)"
customer_id:
type: string
required: true
pii: true
classification: confidential
status:
type: string
required: true
enum: ["CREATED", "CONFIRMED", "SHIPPED", "DELIVERED", "CANCELLED"]
total_amount:
type: decimal
required: true
description: "주문 총액 (할인 적용 후)"
quality:
- type: range
min: 0
currency:
type: string
required: true
pattern: "^[A-Z]{3}$"
created_at:
type: timestamp
required: true
quality:
type: SodaCL
specification:
checks for orders_fact:
- row_count > 0
- missing_count(order_id) = 0
- duplicate_count(order_id) = 0
- invalid_count(total_amount) = 0:
valid min: 0
- freshness(created_at) < 10m
servicelevels:
availability:
percentage: "99.9%"
retention:
period: "3 years"
unlimited: false
latency:
threshold: "5 minutes"
percentile: "p99"
Computational Governance: 정책 자동화
# OPA (Open Policy Agent)를 활용한 거버넌스 자동화
# policy/data_product_policy.rego
package datamesh.governance
# 모든 데이터 제품은 소유자가 있어야 함
deny[msg] {
input.kind == "DataProduct"
not input.metadata.owner
msg := "데이터 제품에 소유자(owner)가 지정되지 않았습니다"
}
# PII 컬럼은 반드시 암호화 설정이 있어야 함
deny[msg] {
input.kind == "DataProduct"
field := input.spec.schema.fields[_]
field.pii == true
not field.encryption
msg := sprintf("PII 컬럼 '%s'에 암호화 설정이 없습니다", [field.name])
}
# SLA 가용성은 최소 99% 이상이어야 함
deny[msg] {
input.kind == "DataProduct"
availability := to_number(trim_suffix(input.spec.sla.availability, "%"))
availability < 99.0
msg := sprintf("SLA 가용성이 %.1f%%로 최소 기준(99%%) 미만입니다", [availability])
}
# 글로벌 식별자 표준 준수
deny[msg] {
input.kind == "DataProduct"
input.spec.global_standards.id_format != "UUID v4"
msg := "ID 형식이 글로벌 표준(UUID v4)과 일치하지 않습니다"
}
# 타임스탬프는 ISO 8601 UTC
deny[msg] {
input.kind == "DataProduct"
input.spec.global_standards.timestamp_format != "ISO 8601 UTC"
msg := "타임스탬프 형식이 글로벌 표준(ISO 8601 UTC)과 일치하지 않습니다"
}
# CI/CD에서 정책 검증
# .github/workflows/data-product-ci.yaml
name: Data Product CI
on:
pull_request:
paths:
- 'data-products/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate Data Product Spec
run: |
datamesh-cli validate dataproduct.yaml
- name: Check Schema Compatibility
run: |
datamesh-cli schema check \
--subject orders-fact-value \
--schema schema/v2.avsc \
--compatibility BACKWARD
- name: Run OPA Policy Checks
uses: open-policy-agent/opa-action@v2
with:
policy: policy/
input: dataproduct.yaml
- name: Run Data Quality Tests
run: |
datamesh-cli quality test \
--expectations quality/expectations.yaml \
--sample-data tests/fixtures/
- name: Generate Documentation
run: |
datamesh-cli docs generate \
--output docs/ \
--format markdown
7. 구현 로드맵: 5단계
Phase 1: 평가 및 파일럿 (2-3개월)
phase_1_assess:
goals:
- "현재 데이터 아키텍처 평가"
- "Data Mesh 적합성 판단"
- "파일럿 도메인 선정"
activities:
- name: "데이터 성숙도 평가"
description: "현재 데이터 인프라, 거버넌스, 조직 역량 평가"
- name: "도메인 매핑"
description: "비즈니스 도메인과 데이터 소유권 매핑"
- name: "파일럿 도메인 선정"
criteria:
- "데이터 소유권이 명확한 도메인"
- "기존 데이터 파이프라인이 있는 도메인"
- "열정적인 팀이 있는 도메인"
- name: "성공 지표 정의"
metrics:
- "데이터 제품 배포 리드 타임"
- "데이터 소비자 만족도"
- "데이터 품질 점수"
deliverables:
- "Data Mesh 적합성 보고서"
- "파일럿 도메인 선정 문서"
- "MVP 범위 정의"
Phase 2: 플랫폼 기반 구축 (3-4개월)
phase_2_platform:
goals:
- "셀프서비스 플랫폼 MVP"
- "데이터 제품 템플릿"
- "기본 거버넌스 프레임워크"
platform_mvp:
- "데이터 제품 scaffold CLI"
- "스키마 레지스트리"
- "기본 데이터 카탈로그"
- "CI/CD 파이프라인 템플릿"
- "모니터링 대시보드"
governance_foundation:
- "글로벌 표준 문서 (식별자, 타임스탬프 등)"
- "데이터 분류 체계"
- "기본 접근 제어 정책"
Phase 3: 파일럿 실행 (3-4개월)
phase_3_pilot:
goals:
- "2-3개 도메인에서 데이터 제품 배포"
- "플랫폼 피드백 수집"
- "프로세스 검증"
pilot_domains:
domain_1:
name: "주문"
data_products: ["orders-fact", "order-items-dim"]
team_size: 6
domain_2:
name: "고객"
data_products: ["customer-profiles", "customer-events"]
team_size: 5
learning_goals:
- "셀프서비스 플랫폼 사용성"
- "데이터 제품 개발 워크플로"
- "도메인 간 데이터 공유 패턴"
- "거버넌스 자동화 효과"
Phase 4: 확장 (6-12개월)
phase_4_scale:
goals:
- "전사적 데이터 제품 확산"
- "고급 거버넌스 자동화"
- "데이터 마켓플레이스"
scaling_strategy:
- "도메인 챔피언 프로그램"
- "내부 데이터 제품 인증 제도"
- "성공 사례 공유"
- "교육 및 멘토링"
Phase 5: 최적화 (지속)
phase_5_optimize:
goals:
- "지속적 개선"
- "고급 기능 도입"
- "문화 정착"
advanced_capabilities:
- "AI/ML 기반 데이터 품질 자동 감지"
- "자동 리니지 추적"
- "데이터 제품 추천 시스템"
- "비용 최적화 자동화"
8. 기술 스택
Datamesh Manager
datamesh_manager:
description: "Data Mesh 구현을 위한 전용 관리 플랫폼"
features:
- "데이터 제품 카탈로그"
- "데이터 계약 관리"
- "도메인 맵 시각화"
- "거버넌스 정책 관리"
integration:
- "DataHub"
- "dbt"
- "Great Expectations"
- "Apache Kafka"
DataHub
# DataHub를 데이터 카탈로그로 활용
# docker-compose.yaml 예시 (간략화)
services_config = """
services:
datahub-gms:
image: linkedin/datahub-gms:latest
ports:
- "8080:8080"
environment:
- EBEAN_DATASOURCE_URL=jdbc:mysql://mysql:3306/datahub
- KAFKA_BOOTSTRAP_SERVER=broker:29092
datahub-frontend:
image: linkedin/datahub-frontend-react:latest
ports:
- "9002:9002"
datahub-actions:
image: linkedin/datahub-actions:latest
"""
Unity Catalog (Databricks)
-- Unity Catalog로 데이터 제품 관리
-- 카탈로그 = 도메인
CREATE CATALOG IF NOT EXISTS order_domain;
CREATE SCHEMA IF NOT EXISTS order_domain.data_products;
-- 데이터 제품 테이블
CREATE TABLE order_domain.data_products.orders_fact (
order_id STRING NOT NULL COMMENT '고유 주문 ID (UUID v4)',
customer_id STRING NOT NULL COMMENT '고객 ID',
status STRING NOT NULL COMMENT '주문 상태',
total_amount DECIMAL(10, 2) NOT NULL COMMENT '주문 총액',
currency STRING NOT NULL COMMENT 'ISO 4217 통화 코드',
created_at TIMESTAMP NOT NULL COMMENT '주문 생성 시각',
processed_at TIMESTAMP COMMENT '데이터 처리 시각'
)
USING DELTA
PARTITIONED BY (date_trunc('day', created_at))
COMMENT '실시간 주문 이벤트 기반 팩트 데이터 제품'
TBLPROPERTIES (
'data_product.domain' = 'order',
'data_product.owner' = 'order-squad',
'data_product.version' = '2.1.0',
'data_product.sla.freshness' = '5 minutes',
'data_product.sla.availability' = '99.9%'
);
-- 접근 권한 관리
GRANT SELECT ON TABLE order_domain.data_products.orders_fact
TO `marketing-analytics-team`;
GRANT SELECT ON TABLE order_domain.data_products.orders_fact
TO `finance-reporting-team`;
9. 조직 변화
팀 토폴로지
Data Mesh는 기술 변화뿐 아니라 조직 변화를 요구합니다.
┌─────────────────────────────────────────────┐
│ Data Mesh 팀 토폴로지 │
├─────────────────────────────────────────────┤
│ │
│ [플랫폼 팀] │
│ - 셀프서비스 플랫폼 구축/운영 │
│ - 인프라 추상화 │
│ - 도구 및 템플릿 제공 │
│ - Enabling Team 역할 │
│ │
│ [도메인 팀 (Stream-aligned)] │
│ - 데이터 제품 소유 및 운영 │
│ - 비즈니스 도메인 전문성 │
│ - 각 팀에 Data Product Owner 포함 │
│ │
│ [거버넌스 팀 (Enabling)] │
│ - 글로벌 표준 정의 │
│ - 정책 자동화 도구 개발 │
│ - 도메인 팀 지원 및 교육 │
│ │
│ [데이터 제품 소유자 커뮤니티] │
│ - 교차 도메인 조정 │
│ - 모범 사례 공유 │
│ - 표준 발전 │
│ │
└─────────────────────────────────────────────┘
Data Product Owner 역할
data_product_owner:
description: "데이터 제품의 전체 수명주기를 관리하는 역할"
responsibilities:
strategic:
- "데이터 제품 로드맵 관리"
- "소비자 요구사항 파악"
- "데이터 제품 가치 측정"
operational:
- "SLA 정의 및 모니터링"
- "데이터 품질 관리"
- "소비자 지원"
governance:
- "데이터 계약 관리"
- "접근 권한 승인"
- "글로벌 표준 준수"
skills:
- "도메인 비즈니스 지식"
- "데이터 모델링"
- "제품 관리"
- "기본적인 데이터 엔지니어링"
collaboration:
with_platform_team: "플랫폼 기능 요구사항 전달"
with_consumers: "데이터 제품 사용 지원"
with_governance: "정책 피드백 및 개선 제안"
10. 도전 과제와 안티패턴
흔한 안티패턴
anti_patterns:
- name: "이름만 Data Mesh"
description: "조직 변화 없이 기술만 분산시키는 경우"
symptom: "중앙 데이터 팀이 여전히 모든 것을 통제"
fix: "도메인 팀에 진정한 소유권 부여, 플랫폼 팀 분리"
- name: "거버넌스 없는 분산"
description: "표준 없이 각 도메인이 독자적으로 진행"
symptom: "데이터 사일로 증가, 상호 운용성 저하"
fix: "연합 거버넌스 위원회 구성, 글로벌 표준 정립"
- name: "플랫폼 없는 분산"
description: "셀프서비스 플랫폼 없이 각 팀에 책임만 부여"
symptom: "각 팀이 인프라 구축에 시간 낭비, 중복 투자"
fix: "플랫폼 팀을 먼저 구성하고 MVP 플랫폼 제공"
- name: "모든 데이터를 제품으로"
description: "내부 임시 데이터까지 데이터 제품으로 만드는 경우"
symptom: "과도한 오버헤드, 팀 피로감"
fix: "외부 소비 가치가 있는 데이터만 제품화"
- name: "빅뱅 전환"
description: "전사적으로 한 번에 Data Mesh 전환 시도"
symptom: "변화 저항, 혼란, 실패"
fix: "파일럿부터 시작하여 점진적 확산"
주요 도전 과제
| 도전 과제 | 설명 | 대응 전략 |
|---|---|---|
| 도메인 팀 역량 | 데이터 엔지니어링 경험 부족 | 플랫폼 추상화 + Enabling 팀 지원 |
| 중복 투자 | 각 도메인이 유사한 인프라 구축 | 셀프서비스 플랫폼으로 표준화 |
| 교차 도메인 쿼리 | 여러 도메인 데이터 결합 어려움 | 집합체 정렬 도메인 + 데이터 가상화 |
| 비용 증가 | 분산으로 인한 인프라 비용 증가 | FinOps 통합, 비용 태깅 |
| 문화 변화 | 데이터 소유권 문화 부재 | 경영진 지원, 교육, 인센티브 |
11. 사례 연구
Zalando: Data Mesh 선구자
zalando_case:
background:
- "유럽 최대 온라인 패션 플랫폼"
- "4,900만+ 고객, 수천 개 브랜드"
- "200+ 데이터 소스"
challenges_before:
- "중앙 데이터 팀이 병목"
- "데이터 요청 백로그 수개월"
- "도메인 지식 손실"
implementation:
- "도메인별 데이터 소유권 전환"
- "셀프서비스 데이터 인프라 (Data Tooling)"
- "데이터 제품 표준 정의"
- "연합 거버넌스 위원회"
results:
- "데이터 제품 배포 시간: 수주 -> 수일"
- "데이터 품질 향상"
- "도메인 팀 자율성 증가"
Netflix: 데이터 플랫폼 분산화
netflix_case:
background:
- "글로벌 스트리밍 서비스"
- "2억+ 구독자"
- "방대한 A/B 테스트 데이터"
approach:
- "중앙 데이터 플랫폼에서 시작"
- "점진적으로 도메인 소유권 전환"
- "강력한 셀프서비스 플랫폼 (Metacat, Dataflow)"
- "데이터 품질 자동화"
key_tools:
- "Metacat: 통합 메타데이터 관리"
- "Dataflow: 데이터 파이프라인 오케스트레이션"
- "Dataframe: 자동 데이터 품질 검증"
Intuit: 데이터 민주화
intuit_case:
background:
- "TurboTax, QuickBooks, Mint 등 금융 소프트웨어"
- "1억+ 고객"
- "높은 데이터 규제 요구사항"
approach:
- "Data Mesh + AI Platform 통합"
- "도메인별 데이터 자산 정의"
- "자동화된 컴플라이언스 검증"
- "내부 데이터 마켓플레이스"
results:
- "데이터 과학자 생산성 3배 향상"
- "규제 준수 자동화"
- "도메인 간 데이터 활용 증가"
12. 퀴즈
Q1. Data Mesh의 4가지 원칙이 아닌 것은?
정답: 중앙 데이터 레이크
Data Mesh의 4가지 원칙:
- 도메인 소유권 (Domain Ownership)
- 데이터를 제품으로 (Data as a Product)
- 셀프서비스 데이터 플랫폼 (Self-Serve Data Platform)
- 연합 컴퓨테이셔널 거버넌스 (Federated Computational Governance)
중앙 데이터 레이크는 Data Mesh가 해결하려는 기존 중앙집중 패턴입니다.
Q2. 소스 정렬 도메인, 집합체 정렬 도메인, 소비자 정렬 도메인의 차이는?
**소스 정렬 도메인(Source-aligned)**은 비즈니스 사실을 생성하는 도메인입니다(예: 주문, 결제).
**집합체 정렬 도메인(Aggregate-aligned)**은 여러 소스를 결합하여 더 풍부한 뷰를 제공합니다(예: Customer 360).
**소비자 정렬 도메인(Consumer-aligned)**은 특정 소비 패턴에 최적화된 도메인입니다(예: 마케팅 분석).
이 세 가지 유형은 데이터의 흐름 방향에 따라 구분됩니다.
Q3. Data Fabric과 Data Mesh의 근본적인 차이는?
Data Fabric은 기술 중심 접근입니다. AI/ML과 메타데이터 자동화를 통해 중앙에서 데이터 통합을 관리합니다. 기존 조직 구조를 유지합니다.
Data Mesh는 조직 중심 접근입니다. 데이터 소유권을 도메인 팀에 분산시키고, 조직 구조의 변화를 요구합니다.
두 접근법은 상호 배타적이 아니며, Data Mesh의 셀프서비스 플랫폼 내에 Data Fabric 기술을 활용할 수 있습니다.
Q4. Computational Governance란 무엇이며 왜 중요한가?
Computational Governance는 거버넌스 정책을 코드로 자동화하여 실행하는 것입니다.
수동 검토 대신 CI/CD 파이프라인에서 OPA(Open Policy Agent) 같은 도구로 정책 준수를 자동 검증하고, 런타임에 SLA를 모니터링하며, PII를 자동 탐지합니다.
중요한 이유: 분산된 환경에서 수동 거버넌스는 확장이 불가능합니다. 도메인 수가 늘어날수록 자동화 없이는 일관성을 유지할 수 없습니다.
Q5. Data Mesh 도입의 가장 흔한 안티패턴 3가지는?
-
이름만 Data Mesh: 조직 변화 없이 기술만 분산. 중앙 팀이 여전히 통제하면서 Data Mesh라고 부름.
-
거버넌스 없는 분산: 표준과 정책 없이 각 도메인이 독자적으로 진행. 데이터 사일로만 증가.
-
빅뱅 전환: 전사적으로 한 번에 전환 시도. 파일럿 없이 모든 도메인을 동시에 전환하면 실패 확률이 매우 높음.
대응: 파일럿부터 시작하고, 플랫폼과 거버넌스를 먼저 준비하며, 진정한 조직 변화를 동반해야 합니다.
참고 자료
- Dehghani, Z. (2022). Data Mesh: Delivering Data-Driven Value at Scale. O'Reilly Media.
- Dehghani, Z. (2019). "How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh." ThoughtWorks Blog.
- Dehghani, Z. (2020). "Data Mesh Principles and Logical Architecture." ThoughtWorks Blog.
- Machado, I. et al. (2022). "Data Mesh in Practice." InfoQ.
- Datamesh Architecture. (2025). Data Mesh Architecture Website. https://www.datamesh-architecture.com/
- DataHub Project. (2025). DataHub Documentation. https://datahubproject.io/
- Data Contract Specification. (2025). https://datacontract.com/
- Zalando Engineering Blog. (2023). "Data Mesh at Zalando."
- Netflix Technology Blog. (2024). "Evolving Netflix's Data Platform."
- Databricks. (2025). "Unity Catalog Documentation."
- Great Expectations Documentation. (2025). https://docs.greatexpectations.io/
- Open Policy Agent. (2025). https://www.openpolicyagent.org/
- Starburst. (2024). "Data Products and Data Mesh." https://www.starburst.io/
- Thoughtworks Technology Radar. (2025). "Data Mesh."