왜 데이터 파이프라인은 커지면 무너지는가

데이터 파이프라인은 대부분 단순한 스크립트 하나로 시작합니다. 그런데 데이터셋이 늘어나고 워크플로우가 3개, 5개, 10개로 늘어나는 순간부터 문제가 터지기 시작하죠.

  • 변환 로직 복붙 지옥: format_date 함수 하나를 고치려면 7개 스크립트를 동시에 수정해야 합니다.
  • 의도가 코드에 묻힘: "이 파이프라인이 대체 뭘 하는 거지?"를 알려면 스크립트를 처음부터 끝까지 읽어야 해요.
  • 늦은 검증: 스키마 오류, 권한 문제, 잘못된 컬럼 매핑이 런타임 중간에 터집니다. 헬스케어·금융·제약처럼 규제가 강한 환경에서는 이게 곧 감사 리스크로 이어져요.

핵심 원인은 단 하나입니다. "무엇을(What)"과 "어떻게(How)"가 같은 스크립트 안에 뒤섞여 있기 때문이에요. 이 글에서는 이 둘을 분리하는 Specification-Driven Composition(스펙 기반 컴포지션) 패턴을 다뤄요. 근거자료는 AWS Architecture Blog 원문에서 확인할 수 있습니다.

Architecture diagram showing three-layer specification-driven composition pattern for data pipelines on AWS cloud Technical Structure Concept

3-레이어 아키텍처: Intent / Composition / Processing

스펙 기반 컴포지션은 워크플로우를 세 개의 레이어로 쪼갭니다.

레이어역할산출물
Intent (의도)워크플로우가 '무엇을' 해야 하는지 정의JSON/YAML Specification
Composition (조립)스펙을 검증하고 실행 가능한 파이프라인으로 컴파일Amazon States Language (ASL)
Processing (실행)재사용 가능한 변환 단계를 순차 실행Lambda Capability Processors

핵심 컴포넌트 4가지는 다음과 같아요.

  1. Specification — 데이터셋, 매핑, 변환을 기술한 선언적 문서 (버전 관리 대상)
  2. Composer — 스펙을 읽고 capability 존재 여부를 검증한 뒤 ASL로 컴파일 (변환 자체는 하지 않음)
  3. Capability Registry — 재사용 가능한 변환 함수의 메타데이터 저장소 (OpenSearch 기반, 풀텍스트/시맨틱 검색 지원)
  4. Capability Pipeline — 조립된 Step Functions가 각 Lambda 프로세서를 순차 호출

스펙 예시 (JSON)

{
  "source": ["raw_orders"],
  "target": ["curated_orders"],
  "mappings": [
    {
      "source_field": "order_date",
      "target_field": "order_date_iso",
      "capability": "format_date@1.2.0",
      "params": { "format": "ISO8601" }
    },
    {
      "source_field": "amount",
      "target_field": "amount_usd",
      "capability": "normalize_currency@2.0.1",
      "params": { "target_currency": "USD" }
    }
  ],
  "preprocessing": [
    { "capability": "standardize_columns@1.0.0" }
  ]
}

Composer의 핵심 로직 (의사 코드)

# 스펙을 읽고, capability를 검증하고, Step Functions를 실행합니다.
def compose_pipeline(spec_s3_key: str) -> str:
    spec = load_json_from_s3(spec_s3_key)
    validate_against_schema(spec)  # 스키마 검증 실패 시 즉시 중단

    for mapping in spec["mappings"]:
        cap_id = mapping["capability"]  # 예: "format_date@1.2.0"
        meta = opensearch.lookup(cap_id)  # ARN, I/O 스키마, 권한 경계 조회
        if meta is None:
            raise CapabilityNotFound(cap_id)  # 런타임 이전에 실패

    asl = build_state_machine(spec)  # 재사용 가능한 ASL로 컴파일
    return stepfunctions.start_execution(asl)

포인트는 컴포저가 변환을 수행하지 않는다는 점이에요. 오직 '조립'만 담당하죠. 이 분리가 규제 환경에서 특히 강력한 이유는, 도메인 사용자가 스펙만 작성하고 실행 코드는 시스템이 만든다는 직무 분리(Separation of Duties) 원칙을 자연스럽게 만족시키기 때문입니다.

Serverless AWS workflow with Lambda composer, Step Functions orchestration and OpenSearch capability registry Coding Session Visual

이 패턴이 빛나는 순간 vs. 오버엔지니어링이 되는 순간

잘 맞는 경우

  • 규제 산업 데이터 제출 파이프라인: 예를 들어 임상시험 데이터를 FDA 제출용 SDTM(Study Data Tabulation Model) 포맷으로 변환하는 경우. 분석가가 스펙만 작성하면 컴포저가 검증된 capability로 파이프라인을 조립하므로, 감사 대응 시간이 크게 줄어요.
  • 다중 소스 통합: 월별 재무 리포트를 서로 다른 소스 시스템에서 뽑아내야 하는 경우처럼 변형(variant)이 3개 이상인 파이프라인.
  • 재사용 ETL 프레임워크: 변환 로직을 한 번 구현하고 여러 워크플로우에서 재사용하고 싶을 때.

오버엔지니어링이 되는 경우

  • 1회성 변환: 한 번 돌리고 버릴 스크립트에 컴포저를 붙이는 건 낭비예요.
  • 워크플로우가 3~5개 미만: 중복 제거 효과보다 컴포저·레지스트리 운영 비용이 더 큽니다.

보안·규제 관점 체크리스트

  • 스펙·데이터 S3 버킷에 SSE-KMS(고객 관리 키) 적용, 버킷 정책에 aws:SecureTransport 강제
  • OpenSearch 도메인에 저장 시 암호화 + 노드 간 암호화 활성화
  • 민감도 태깅: 스펙에 "sensitivity": "PHI"처럼 필드 민감도를 태깅하고, capability별 동작(보존/제거/생성)을 레지스트리에 선언 → 컴포저가 자동으로 마스킹 아티팩트(Lake Formation 컬럼 그랜트 등) 생성
  • Capability 버전 고정: 스펙에서 format_date@1.2.0처럼 명시적 버전 참조 → 재현성 확보

한국 개발 생태계에서의 적용 맥락

국내 SI·금융권 환경에서는 이 패턴이 특히 유효해요. 금감원·금융보안원 감사 대응이 필요한 프로젝트에서 "이 파이프라인이 어떤 변환을 수행하는가"를 스펙 문서 한 장으로 설명할 수 있다는 건 엄청난 무기입니다. 다만 국내 환경은 레거시 온프레미스 + 배치 중심인 경우가 많아서, Step Functions 대신 Airflow DAG로 컴파일하는 변형이 더 현실적일 수 있어요. 컴포저의 'ASL 생성' 부분만 'DAG 생성'으로 바꾸면 개념은 그대로 유지됩니다.

Data analyst reviewing JSON specification mapping source fields to target fields in a declarative pipeline Development Concept Image

정리: 파이프라인을 '설정 작업'으로 바꾸는 투자

스펙 기반 컴포지션의 본질은 엔지니어링 작업을 설정 작업으로 전환하는 것이에요. 재사용 가능한 capability 라이브러리와 규율 있는 스펙 포맷에 초기 투자를 하면, 이후 새 파이프라인은 코드 수정 없이 스펙 작성만으로 만들어집니다.

실무에서 바로 시작하는 방법은 이 순서를 추천해요.

  1. 기존 파이프라인 하나를 골라 스펙으로 문서화 — 코드를 안 바꾸고 스펙만 써봐도 인사이트가 나옵니다.
  2. 재사용 가능한 변환 함수 3~5개를 capability로 등록 — Registry부터 만들어보세요.
  3. 컴포저 프로토타입 작성 — S3 업로드 → Lambda → Step Functions 흐름으로 최소 구현.
  4. 월별 재무 리포트처럼 variant가 3개 이상인 파이프라인에 적용 — 온보딩 시간을 측정해서 효과를 정량화하세요.

이 패턴의 한계

  • **컴포저 자체가 단일 실패점(SPOF)**이 될 수 있어요. 컴포저 장애는 모든 파이프라인 신규 배포를 마비시킵니다.
  • 스펙 스키마 진화 관리가 새로운 부담입니다. 스키마 버저닝과 마이그레이션 전략 없이는 스펙이 곧 기술 부채가 돼요.
  • 디버깅 난이도 상승: "왜 이 필드가 이렇게 변환됐지?"를 추적하려면 스펙 → ASL → Lambda 3단계를 넘나들어야 합니다. 관측성(OpenTelemetry 트레이싱)을 처음부터 설계에 넣으세요.

다음 단계 학습 방향

함께 보면 좋은 글

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.