<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>불곰</title>
    <link>https://brownbears.tistory.com/</link>
    <description>이것저것</description>
    <language>ko</language>
    <pubDate>Wed, 24 Jun 2026 16:45:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>불곰1</managingEditor>
    <image>
      <title>불곰</title>
      <url>https://tistory1.daumcdn.net/tistory/1954575/attach/09aa2e0ab9af4a0fbf5d108ea5142763</url>
      <link>https://brownbears.tistory.com</link>
    </image>
    <item>
      <title>OLAP 분석 데이터베이스 완전 비교 가이드</title>
      <link>https://brownbears.tistory.com/751</link>
      <description>&lt;h1&gt;1. OLAP DB 분류 개요&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.1 데이터 저장 방식에 따른 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분류 설명 대표 제품&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자체 저장형 OLAP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터를 직접 저장&amp;middot;관리, 전용 스토리지 엔진 보유&lt;/td&gt;
&lt;td&gt;Druid, Pinot, ClickHouse, StarRocks, Doris&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MPP 쿼리 엔진형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터 비저장, 외부 스토리지(HDFS, S3 등)에 쿼리만 실행&lt;/td&gt;
&lt;td&gt;Trino, Presto, Spark SQL, Impala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;임베디드/경량형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;프로세스 내 실행, 서버리스&lt;/td&gt;
&lt;td&gt;DuckDB, chDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스트리밍 DB형&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실시간 스트림 처리 + OLAP 쿼리 통합&lt;/td&gt;
&lt;td&gt;RisingWave&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.2 Apache Impala의 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Impala는 &lt;b&gt;MPP 쿼리 엔진 계열&lt;/b&gt;에 속합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 직접 저장하지 않음 &amp;rarr; HDFS, HBase, S3 등 외부 스토리지에 직접 쿼리&lt;/li&gt;
&lt;li&gt;Hadoop 에코시스템과 완전 통합 (Hive 메타스토어, YARN 등 공유)&lt;/li&gt;
&lt;li&gt;구조: Shared-Nothing MPP 아키텍처이나, Hadoop 위에서 동작하는 SQL 레이어&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Trino/Presto와 유사한 포지셔닝&lt;/b&gt;, 단 Hadoop 전용으로 설계됨&lt;/li&gt;
&lt;li&gt;2025년 기준 Iceberg, Parquet, ORC 등 오픈 포맷 지원 강화 (4.5 버전)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한계&lt;/b&gt;: Hadoop 의존성이 강해 클라우드 네이티브 환경에서 입지 약화 중&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;결론&lt;/b&gt;: Impala는 &quot;MPP 쿼리 엔진&quot; 문서에 정리된 Trino/Presto와 같은 범주. 자체 저장형 OLAP이 아님.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Apache Druid&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2011년 (Metamarkets), 2015년 Apache 편입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 실시간 스트리밍 + 이벤트 기반 OLAP 데이터베이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: 수십억~수조 개 이벤트 행의 밀리초급 슬라이스-앤-다이스 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: Netflix, Airbnb, Twitter, Lyft, Nielsen&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;마이크로서비스 구조&lt;/b&gt;: Master(Coordinator, Overlord), Query(Broker, Router), Data(Historical, MiddleManager, Indexer), Deep Storage&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컬럼형 저장&lt;/b&gt; + &lt;b&gt;시간 파티셔닝&lt;/b&gt; (Time-based Partitioning이 1차 기준)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Roaring Bitmap 인덱스&lt;/b&gt;: 멀티 컬럼 필터링 고속화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deep Storage&lt;/b&gt;: S3/HDFS 등 외부 스토리지에 세그먼트 영구 보존 &amp;rarr; 장애 복구 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Streaming 수집&lt;/b&gt;: Apache Kafka, Amazon Kinesis 네이티브 연동&lt;/li&gt;
&lt;li&gt;Scatter/Gather 쿼리 실행 모델 (데이터를 메모리/로컬 스토리지에 사전 로드)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초고속 실시간 스트리밍 수집 + 동시 쿼리 (수집 즉시 쿼리 가능)&lt;/li&gt;
&lt;li&gt;시계열/이벤트 데이터에 최적화된 압축 및 인덱싱&lt;/li&gt;
&lt;li&gt;자동 데이터 롤업(Pre-aggregation)으로 스토리지/쿼리 비용 절감&lt;/li&gt;
&lt;li&gt;수평 확장성 우수, 컴포넌트별 독립 스케일링 가능&lt;/li&gt;
&lt;li&gt;딥 스토리지 기반 내결함성&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Upsert 미지원&lt;/b&gt;: 실시간 업데이트/삭제 불가, append-only 설계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 복잡도 高&lt;/b&gt;: 다수의 마이크로서비스(6~8개 프로세스) 관리 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡 JOIN 약함&lt;/b&gt;: 다중 테이블 조인 성능이 ClickHouse/StarRocks 대비 미흡&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL 표준 준수 부분적&lt;/b&gt;: 일부 SQL 기능 미지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 곡선 가파름&lt;/b&gt;: 아키텍처 이해 및 튜닝에 상당한 노력 필요&lt;/li&gt;
&lt;li&gt;근래 Apache Pinot, StarRocks 대비 커뮤니티 성장세 둔화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클릭스트림, 광고 분석, IoT 이벤트 스트림 실시간 대시보드&lt;/li&gt;
&lt;li&gt;수십억 행 이벤트 테이블의 고동시성 slice-and-dice 분석&lt;/li&gt;
&lt;li&gt;업데이트 없는 append-only 로그/이벤트 데이터&lt;/li&gt;
&lt;li&gt;시간 범위 기반 분석이 주된 워크로드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트별 독립 스케일링 (Historical 서버 추가로 쿼리 용량 확장)&lt;/li&gt;
&lt;li&gt;페타바이트 규모 운영 사례 존재 (Netflix 등)&lt;/li&gt;
&lt;li&gt;클라우드: Imply Cloud (상용), AWS/GCP 자체 운영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Apache Pinot&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2013년 (LinkedIn), 2015년 Apache 편입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 사용자 직면(User-Facing) 실시간 분석 전문 OLAP DB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: 초저지연 + 초고동시성 &amp;mdash; 수십만 QPS에서 일관된 밀리초 응답&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: LinkedIn, Uber, Stripe, Walmart, WeChat&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴포넌트&lt;/b&gt;: Controller, Broker, Server, Minion (각 독립 서비스)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세그먼트 기반 분산 저장&lt;/b&gt;: Immutable segments + Consuming segments(실시간)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 인덱스 지원&lt;/b&gt;: Inverted, Sorted, Range, Text(Lucene), JSON, &lt;b&gt;Star-Tree(사전집계)&lt;/b&gt;, N-gram, HNSW(벡터)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Multi-Stage Query Engine&lt;/b&gt;: 복잡한 JOIN/서브쿼리 지원 강화 (v1.0+)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Upsert 지원&lt;/b&gt;: Primary Key 기반 실시간 업데이트 가능&lt;/li&gt;
&lt;li&gt;Kafka, Pulsar, Kinesis, 배치(S3/Hadoop) 수집 모두 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;최고 수준의 동시성&lt;/b&gt;: 100,000+ QPS에서 일관된 성능&lt;/li&gt;
&lt;li&gt;ClickHouse 대비 쿼리 속도 4배, Druid 대비 5~7배 빠름 (일부 벤치마크)&lt;/li&gt;
&lt;li&gt;Star-Tree 인덱스로 집계 쿼리 초고속 처리&lt;/li&gt;
&lt;li&gt;Upsert 지원으로 가변 데이터 실시간 반영&lt;/li&gt;
&lt;li&gt;벡터 검색(HNSW) 내장으로 AI/RAG 파이프라인 통합&lt;/li&gt;
&lt;li&gt;2026년 기준 활발한 개발 (v1.5.0 &amp;mdash; Kafka 4.x, Time Series Engine, Federation 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;운영 복잡도 높음&lt;/b&gt;: Druid와 유사하게 다수 컴포넌트 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡 쿼리 약점&lt;/b&gt;: 복잡한 다중 JOIN은 StarRocks/ClickHouse 대비 불리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;학습 비용&lt;/b&gt;: Star-Tree 등 독자 인덱스 이해 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생태계&lt;/b&gt;: Druid/ClickHouse보다 커뮤니티 규모 작음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자 직면 분석 API&lt;/b&gt;: 앱/서비스 내 실시간 대시보드 (수만 동시 사용자)&lt;/li&gt;
&lt;li&gt;LinkedIn &quot;Who viewed my profile&quot;, Uber 실시간 지표, Stripe 결제 분석&lt;/li&gt;
&lt;li&gt;높은 QPS + 낮은 지연이 동시에 요구되는 환경&lt;/li&gt;
&lt;li&gt;이상 탐지, 실시간 메트릭 모니터링&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StarTree Cloud (상용 관리형), Apache 오픈소스&lt;/li&gt;
&lt;li&gt;수백 노드 클러스터 운영 사례 (LinkedIn 1조+ 행)&lt;/li&gt;
&lt;li&gt;컴포넌트별 독립 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. ClickHouse&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2016년 (Yandex 오픈소스 공개)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 범용 고성능 컬럼형 분석 OLAP DB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: 단순성 + 극한 쿼리 속도 + 낮은 운영 비용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: Cloudflare, Uber, ByteDance, eBay, Spotify, Discord&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컬럼형 저장&lt;/b&gt; (MergeTree 패밀리): MergeTree, ReplacingMergeTree, AggregatingMergeTree 등 특수 테이블 엔진&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터화 실행 엔진&lt;/b&gt; (SIMD 최적화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공유 없는(Shared-Nothing) 아키텍처&lt;/b&gt; &amp;rarr; 클라우드 네이티브 SharedCatalog(2025) 도입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;강력한 압축&lt;/b&gt;: LZ4, ZSTD, 전용 코덱&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분산 쿼리&lt;/b&gt;: Distributed 테이블 엔진으로 클러스터 투명 쿼리&lt;/li&gt;
&lt;li&gt;2025년: SharedCatalog(중앙집중 메타데이터), 벡터 검색(HNSW) 프로덕션 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 테이블 스캔 최강&lt;/b&gt;: 수십억 행 풀스캔도 초 단위 응답&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뛰어난 압축률&lt;/b&gt;: 타 DB 대비 5~10배 공간 절약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쉬운 도입&lt;/b&gt;: 단일 바이너리, 설정 단순, 최소 컴포넌트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;광범위한 SQL 지원&lt;/b&gt;: 복잡한 집계&amp;middot;분석함수&amp;middot;Window function&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활발한 생태계&lt;/b&gt;: GitHub Star 1위급, ClickHouse Cloud(관리형), 풍부한 커넥터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClickHouse Cloud&lt;/b&gt;: 서버리스, 자동 스케일링&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;JOIN 성능 한계&lt;/b&gt;: 복잡한 다중 JOIN 시 메모리 압박, StarRocks 대비 불리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Upsert/Update 비효율&lt;/b&gt;: MUTATION 연산이 무거움, 빈번한 업데이트 비적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;진정한 실시간 수집 아님&lt;/b&gt;: 배치 인서트 &amp;rarr; 데이터 가시화에 약간의 지연&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스키마 변경 비용&lt;/b&gt;: 구조 변경 시 전체 재로드 필요한 경우 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OLTP 부적합&lt;/b&gt;: 트랜잭션 처리 미지원&lt;/li&gt;
&lt;li&gt;높은 동시성 환경(100+ 동시 쿼리)에서 성능 저하 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로그/이벤트 분석&lt;/b&gt;: 웹 로그, 애플리케이션 로그 대용량 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시계열 메트릭&lt;/b&gt;: 시스템 모니터링, APM, 광고 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단일 넓은 테이블&lt;/b&gt;: 수십~수백 컬럼 풀스캔 집계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애드혹 분석&lt;/b&gt;: 개발자/분석가 자유 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비용 효율 우선&lt;/b&gt;: 스토리지&amp;middot;컴퓨팅 비용 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClickHouse Cloud (서버리스, 자동 스케일링, 페타바이트급)&lt;/li&gt;
&lt;li&gt;오픈소스 자체 운영: 수평 샤딩+리플리케이션&lt;/li&gt;
&lt;li&gt;2025년 SharedCatalog로 클라우드 네이티브 아키텍처 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. StarRocks&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2020년 (Apache Doris 포크), 2022년 오픈소스 전환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 고성능 MPP + 실시간 분석 통합 DB (Lakehouse 지원)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: JOIN 최강 + 고동시성 + 레이크하우스 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: Airbnb, Shopee, &lt;a href=&quot;http://JD.com&quot;&gt;JD.com&lt;/a&gt;, Xiaomi&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MPP 아키텍처&lt;/b&gt; (분산 조인 전문)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터화 실행 엔진&lt;/b&gt; + &lt;b&gt;Cost-Based Optimizer(CBO)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Primary Key 테이블&lt;/b&gt;: 실시간 Upsert 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Shared-Data 모드&lt;/b&gt;: S3 호환 오브젝트 스토리지 분리 (레이크하우스)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL 프로토콜 호환&lt;/b&gt;: 기존 MySQL 드라이버/툴 그대로 사용&lt;/li&gt;
&lt;li&gt;External Catalog: Hive, Iceberg, Delta Lake, JDBC 직접 쿼리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡 JOIN 최강&lt;/b&gt;: MPP 설계 + CBO로 다중 테이블 조인 압도적 성능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Upsert 효율&lt;/b&gt;: Primary Key 테이블로 실시간 데이터 변경 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고동시성&lt;/b&gt;: ClickHouse 대비 100배 더 많은 동시 세션에서 P95 sub-second 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레이크하우스 통합&lt;/b&gt;: Iceberg/Delta 직접 쿼리 + 자체 스토리지 혼용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL 호환&lt;/b&gt;: 전환 비용 최소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;올인원&lt;/b&gt;: 스트리밍 수집, 배치, 애드혹, 대시보드 모두 커버&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 테이블 스캔&lt;/b&gt;: 넓은 평탄 테이블 풀스캔은 ClickHouse가 우세&lt;/li&gt;
&lt;li&gt;&lt;b&gt;압축률&lt;/b&gt;: ClickHouse 대비 스토리지 효율 낮음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커뮤니티&lt;/b&gt;: ClickHouse 대비 작은 생태계 (성장 중)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관리형 서비스&lt;/b&gt;: CelerData Cloud (상용), 자체 운영 난이도 중간&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;복잡한 스타 스키마 분석&lt;/b&gt;: 다중 테이블 JOIN이 빈번한 데이터 웨어하우스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 가변 데이터&lt;/b&gt;: CDC 기반 실시간 Upsert 필요 환경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레이크하우스 구축&lt;/b&gt;: S3 + 자체 스토리지 혼용 아키텍처&lt;/li&gt;
&lt;li&gt;&lt;b&gt;높은 동시성 분석&lt;/b&gt;: 수백~수천 동시 쿼리 환경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL 마이그레이션&lt;/b&gt;: 기존 MySQL 스택 분석 레이어 교체&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared-Data 모드로 컴퓨팅&amp;middot;스토리지 독립 확장&lt;/li&gt;
&lt;li&gt;CelerData Cloud (관리형), 오픈소스 자체 운영&lt;/li&gt;
&lt;li&gt;수십 TB ~ 수 PB 운영 사례&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Apache Doris&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2017년 (Baidu 오픈소스), Apache TLP 2022년&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관계&lt;/b&gt;: StarRocks의 원조 (StarRocks는 Doris PMC 멤버들의 포크)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 범용 MPP 분석 DB, Doris vs StarRocks 치열한 경쟁 중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: Baidu, Meituan, Xiaomi (중국 테크 기업 주도)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.2 특징 및 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StarRocks와 유사한 MPP 아키텍처&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL 완벽 호환&lt;/b&gt; (프로토콜, SQL 방언)&lt;/li&gt;
&lt;li&gt;실시간 Upsert (Unique Key 모델)&lt;/li&gt;
&lt;li&gt;Apache Iceberg, Hudi, Delta Lake External Catalog 지원&lt;/li&gt;
&lt;li&gt;스트리밍 수집 (Kafka Routine Load, Stream Load)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.3 Doris vs StarRocks 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 Apache Doris StarRocks&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기원&lt;/td&gt;
&lt;td&gt;원조&lt;/td&gt;
&lt;td&gt;Doris 포크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성능&lt;/td&gt;
&lt;td&gt;양호&lt;/td&gt;
&lt;td&gt;벤치마크 우세&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커뮤니티&lt;/td&gt;
&lt;td&gt;Apache 재단&lt;/td&gt;
&lt;td&gt;독자 오픈소스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리형 서비스&lt;/td&gt;
&lt;td&gt;SelectDB Cloud&lt;/td&gt;
&lt;td&gt;CelerData Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중국 내 인지도&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;글로벌 인지도&lt;/td&gt;
&lt;td&gt;성장 중&lt;/td&gt;
&lt;td&gt;성장 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lakehouse&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;td&gt;더 성숙&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.4 장단점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: Apache 재단의 중립적 거버넌스, MySQL 완벽 호환, 배치+스트리밍 통합, 활발한 중국 커뮤니티&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;: StarRocks 대비 일부 벤치마크 열세, 글로벌 생태계 아직 작음, 문서 일부 중국어 위주&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.5 적합한 사용 사례&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StarRocks와 거의 동일. Apache 재단 거버넌스 선호, 중국 클라우드(알리바바 등) 사용 환경, SelectDB Cloud 선택 시.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. DuckDB&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2018년 (CWI 암스테르담 연구소), 2019년 오픈소스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이선스&lt;/b&gt;: MIT (완전 오픈소스)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 임베디드 in-process 분석 DB &amp;mdash; &quot;분석 분야의 SQLite&quot;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: 서버 없이 애플리케이션/노트북 내에서 즉시 고성능 OLAP 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: 데이터 과학자, 소규모 팀 분석 파이프라인, 임베디드 분석 앱&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;In-process 실행&lt;/b&gt;: Python/R/Node.js/Go/Java 라이브러리로 프로세스 내 임베딩. 별도 서버 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벡터화 실행 엔진&lt;/b&gt;: SIMD 최적화 컬럼형 처리 (ClickHouse와 유사한 방식)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 직접 쿼리&lt;/b&gt;: Parquet, CSV, Arrow, JSON, Iceberg, Delta Lake 파일을 네이티브 쿼리 (복사 없음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MotherDuck&lt;/b&gt;: DuckDB의 서버리스 클라우드 관리형 &amp;mdash; 로컬&amp;harr;클라우드 동일 SQL 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pg_duckdb&lt;/b&gt;: PostgreSQL 내 DuckDB 엔진 임베딩 (DuckDB 분석 엔진을 PG 확장으로 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;chDB&lt;/b&gt;: ClickHouse 엔진의 임베디드 버전 (DuckDB와 경쟁하는 대안)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제로 설치&lt;/b&gt;: pip/npm/CRAN 패키지 설치만으로 즉시 사용 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로컬 분석 최강&lt;/b&gt;: 수억 행 Parquet/CSV 파일 단일 노드에서 초 단위 집계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 이동 없음&lt;/b&gt;: S3, 로컬 파일, Arrow 메모리, Iceberg를 제자리에서 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Iceberg 네이티브&lt;/b&gt;: Iceberg 테이블 읽기/쓰기 지원 &amp;rarr; 레이크하우스 파이프라인 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL 표준 준수&lt;/b&gt;: PostgreSQL 방언 호환 수준의 광범위한 SQL 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커뮤니티 성장&lt;/b&gt;: GitHub 23K+ Star, ADBC/Arrow 생태계 완전 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 노드 한계&lt;/b&gt;: 페타바이트급 분산 처리 불가. 메모리/디스크 용량이 병목&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고동시성 부적합&lt;/b&gt;: 수십 명 이상 동시 쿼리 시 성능 저하 (서버 기반 OLAP DB와 경쟁 불가)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kafka 수집 불가&lt;/b&gt;: 실시간 스트리밍 수집 미지원 &amp;mdash; 파일/배치 기반 워크로드 전용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공유 워크로드 비적합&lt;/b&gt;: 여러 팀이 공유하는 OLAP 서버 역할 부적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MotherDuck 의존&lt;/b&gt;: 클라우드 확장은 MotherDuck 단일 공급자에 의존&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 과학자 로컬 탐색 분석 (Jupyter 노트북 내 Parquet 쿼리)&lt;/li&gt;
&lt;li&gt;소규모 팀 분석 파이프라인 (데이터 변환, ELT 로컬 처리)&lt;/li&gt;
&lt;li&gt;임베디드 분석 앱 (애플리케이션 내 경량 OLAP 엔진)&lt;/li&gt;
&lt;li&gt;MotherDuck을 통한 클라우드 서버리스 분석 (TB급 이하 팀)&lt;/li&gt;
&lt;li&gt;S3/로컬 Iceberg&amp;middot;Parquet 파일 즉석 쿼리 (인프라 없이)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 노드&lt;/b&gt;: RAM + NVMe 디스크 크기가 한계 (수 TB까지 실용적)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MotherDuck&lt;/b&gt;: 서버리스 클라우드로 스케일 아웃, 로컬&amp;harr;클라우드 투명 전환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MotherDuck 가격&lt;/b&gt;: 서버리스 쿼리 기반 과금 &amp;mdash; 소규모 팀에 최적&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한계점&lt;/b&gt;: 100TB 이상 또는 수백 동시 쿼리 &amp;rarr; ClickHouse/StarRocks로 전환 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. RisingWave&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2021년 오픈소스 공개&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이선스&lt;/b&gt;: Apache 2.0 (완전 오픈소스)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 스트리밍 데이터베이스 &amp;mdash; 스트림 처리(Flink 역할) + OLAP 쿼리(DB 역할) 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: Kafka/Kinesis 이벤트를 소비하면서 실시간 Materialized View 유지 + SQL로 즉시 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: 실시간 대시보드, 이상 탐지, 피처 스토어, Flink + OLAP DB 교체&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PostgreSQL 와이어 프로토콜 호환&lt;/b&gt;: 모든 PostgreSQL 드라이버/클라이언트 그대로 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Materialized View 엔진&lt;/b&gt;: Kafka/Kinesis 스트림에서 직접 소비 &amp;rarr; MV 증분 업데이트 &amp;rarr; 항상 쿼리 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ACID 일관성 보장&lt;/b&gt;: 스트리밍 처리 중에도 트랜잭션 일관성 유지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴퓨팅-스토리지 분리&lt;/b&gt;: S3 호환 오브젝트 스토리지 기반 &amp;rarr; 탄력적 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kafka/Kinesis/Pulsar 네이티브 커넥터&lt;/b&gt;: 소스 직접 연결, 별도 Kafka Connect 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sink 지원&lt;/b&gt;: ClickHouse, StarRocks, Iceberg, S3, RDBMS 등 다양한 싱크 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;스트림+쿼리 통합&lt;/b&gt;: Flink(스트림 처리) + OLAP DB(쿼리 서빙)를 단일 시스템으로 대체&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 집계 즉시 쿼리&lt;/b&gt;: MV가 항상 최신 상태 유지 &amp;rarr; 별도 집계 작업 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PostgreSQL 호환&lt;/b&gt;: 기존 PG 생태계(도구&amp;middot;드라이버&amp;middot;ORM) 그대로 활용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 단순화&lt;/b&gt;: Flink + OLAP DB + 중간 Kafka 토픽 제거 &amp;rarr; 파이프라인 복잡도 대폭 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ACID 보장&lt;/b&gt;: 스트리밍 환경에서도 데이터 일관성 확보 (Flink 대비 강점)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;대규모 배치 분석&lt;/b&gt;: 수 PB 히스토리컬 배치 분석은 ClickHouse/StarRocks 대비 미흡&lt;/li&gt;
&lt;li&gt;&lt;b&gt;신생 제품&lt;/b&gt;: 2021년 출시 &amp;mdash; 엔터프라이즈 성숙도&amp;middot;레퍼런스 상대적으로 부족&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스트리밍 특화&lt;/b&gt;: 배치 ETL&amp;middot;대용량 애드혹 분석보다 스트리밍 집계에 최적화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커뮤니티 규모&lt;/b&gt;: ClickHouse/StarRocks 대비 작은 생태계&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실시간 집계 대시보드 (주문 현황, 재고, KPI 실시간 반영)&lt;/li&gt;
&lt;li&gt;이상 탐지&amp;middot;사기 탐지 (실시간 이벤트 &amp;rarr; 즉시 룰 평가)&lt;/li&gt;
&lt;li&gt;피처 스토어 구축 (ML 모델용 실시간 피처 집계&amp;middot;서빙)&lt;/li&gt;
&lt;li&gt;Flink + OLAP DB 아키텍처의 단순화 대안&lt;/li&gt;
&lt;li&gt;실시간 리포트 API (PostgreSQL 호환으로 BI 도구 직접 연결)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수평 확장&lt;/b&gt;: 컴퓨팅 노드 독립 확장 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스토리지 분리&lt;/b&gt;: S3 기반 &amp;rarr; 스토리지 무제한 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RisingWave Cloud&lt;/b&gt;: AWS/GCP/Azure 완전 관리형, BYOC 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;요금&lt;/b&gt;: RWU(1 vCPU 또는 4GB RAM) 단위 과금 + 스토리지 GB/월&lt;/li&gt;
&lt;li&gt;&lt;b&gt;규모&lt;/b&gt;: 수 TB~PB 운영 가능, 단 배치 분석보다 스트리밍 집계에서 최고 효율&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Firebolt&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2020년 (이스라엘 스타트업)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이선스&lt;/b&gt;: 완전 상용 (오픈소스 없음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: 클라우드 네이티브 고성능 서버리스 OLAP&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: 극한 쿼리 속도 + 완전 서버리스 + 인프라 관리 제로&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: 클라우드 네이티브 고성능 분석, 운영 부담 최소화 우선 팀&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Sparse Index&lt;/b&gt;: 세그먼트별 최소/최대값 기반 블록 단위 데이터 스킵으로 I/O 최소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Aggregating Index&lt;/b&gt;: 사전 집계 인덱스 &amp;mdash; 집계 쿼리 즉시 응답&lt;/li&gt;
&lt;li&gt;&lt;b&gt;S3 기반 컴퓨팅-스토리지 완전 분리&lt;/b&gt;: 컴퓨팅과 스토리지 독립 스케일링&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버리스 자동 스케일링&lt;/b&gt;: 워크로드에 따라 엔진 자동 확장/축소/일시정지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AWS/GCP 기반&lt;/b&gt;: 두 클라우드에서 동작, 멀티 리전 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;쿼리 속도 최상위권&lt;/b&gt;: Sparse Index + Aggregating Index 조합으로 ClickHouse 수준 이상 성능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완전 서버리스&lt;/b&gt;: 엔진 미사용 시 자동 일시정지 &amp;rarr; 비용 최소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인프라 관리 불필요&lt;/b&gt;: 클러스터 구성&amp;middot;업그레이드&amp;middot;장애 복구 모두 자동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 완비&lt;/b&gt;: SOC2 Type II, RBAC, MFA(Okta/Auth0 통합), TLS+AES-256&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 엔진&lt;/b&gt;: 개발/테스트/프로덕션 용도별 엔진 분리 운영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;오픈소스 없음&lt;/b&gt;: 완전 상용 제품 &amp;mdash; 벤더 락인 위험, 공개 가격 없음 (영업 문의 필요)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생태계 제한적&lt;/b&gt;: ClickHouse/StarRocks 대비 커넥터&amp;middot;통합 도구 적음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;신생 제품&lt;/b&gt;: 2020년 출시 &amp;mdash; 대형 레퍼런스 상대적으로 부족&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가격 불투명&lt;/b&gt;: 공개 가격표 없음, 사용량에 따라 비용 예측 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라우드 네이티브 환경에서 운영 부담 없이 고성능 OLAP 필요 시&lt;/li&gt;
&lt;li&gt;버스티한 워크로드 (피크 시 자동 확장, 유휴 시 비용 0)&lt;/li&gt;
&lt;li&gt;엔터프라이즈 보안&amp;middot;컴플라이언스 요구 팀 (SOC2, RBAC, MFA)&lt;/li&gt;
&lt;li&gt;빠른 프로토타이핑 &amp;rarr; 프로덕션 전환 (인프라 설정 없이 즉시 시작)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;서버리스 자동 스케일링&lt;/b&gt;: 요청량에 따라 엔진 자동 확장/축소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스토리지&lt;/b&gt;: S3 기반 무제한 확장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 엔진&lt;/b&gt;: 복수 엔진(개발/프로덕션/임시분석) 병렬 운영&lt;/li&gt;
&lt;li&gt;&lt;b&gt;규모&lt;/b&gt;: PB급 데이터 처리 가능, 완전 관리형&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. Tinybird&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.1 개요&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출시&lt;/b&gt;: 2019년 (스페인 스타트업)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이선스&lt;/b&gt;: 완전 상용 (오픈소스 없음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;포지션&lt;/b&gt;: ClickHouse 기반 실시간 분석 API 플랫폼&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 철학&lt;/b&gt;: SQL을 REST API로 자동 변환 &amp;mdash; ClickHouse 인프라 없이 고객 직면 분석 API 즉시 구축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 사용처&lt;/b&gt;: 고객 직면 분석 API, 실시간 대시보드 API, 스트리밍 데이터 API화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.2 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClickHouse 엔진 기반&lt;/b&gt;: 내부적으로 ClickHouse를 사용하여 고성능 컬럼형 쿼리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SQL &amp;rarr; REST API 자동 변환&lt;/b&gt;: .pipe 파일에 SQL을 작성하면 엔드포인트 자동 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 스트리밍 수집&lt;/b&gt;: Kafka, Kinesis, HTTP Events API를 통한 실시간 데이터 수집&lt;/li&gt;
&lt;li&gt;&lt;b&gt;버전 관리&lt;/b&gt;: API&amp;middot;데이터 파이프라인 Git 기반 버전 관리 (CI/CD 통합)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완전 관리형 SaaS&lt;/b&gt;: 인프라 없이 클라우드 웹 콘솔에서 전체 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.3 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;초고속 API 빌딩&lt;/b&gt;: SQL만으로 밀리초급 분석 API 수 분 내 구축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClickHouse 성능&lt;/b&gt;: 내부 엔진이 ClickHouse이므로 단일 테이블 집계 쿼리 최고 수준&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인프라 관리 제로&lt;/b&gt;: ClickHouse 클러스터 운영&amp;middot;튜닝&amp;middot;업그레이드 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발자 친화적&lt;/b&gt;: Git 워크플로우, SQL 기반, REST API 표준 출력&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 수집&lt;/b&gt;: Kafka/HTTP 이벤트 직접 수신 &amp;rarr; 즉시 쿼리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.4 단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;오픈소스 없음&lt;/b&gt;: 완전 SaaS, 벤더 종속&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡 쿼리 한계&lt;/b&gt;: ClickHouse 기반이므로 복잡한 다중 JOIN은 여전히 취약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가격 급증 위험&lt;/b&gt;: 트래픽&amp;middot;데이터 볼륨 증가 시 비용 급증 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커스터마이징 제한&lt;/b&gt;: ClickHouse 직접 운영 대비 설정 자유도 낮음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.5 적합한 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고객 직면 분석 API 빠른 구축 (앱 내 실시간 통계&amp;middot;리포트 API)&lt;/li&gt;
&lt;li&gt;ClickHouse 인프라 직접 운영 없이 ClickHouse 성능 활용 원하는 팀&lt;/li&gt;
&lt;li&gt;스타트업&amp;middot;소규모 팀의 MVP 분석 API (인프라 설정 없이 즉시 시작)&lt;/li&gt;
&lt;li&gt;이벤트 스트리밍 &amp;rarr; REST API 파이프라인 (Kafka &amp;rarr; Tinybird &amp;rarr; 앱)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.6 확장성&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;완전 관리형 SaaS&lt;/b&gt;: Tinybird가 스케일링 자동 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClickHouse 기반&lt;/b&gt;: 내부적으로 ClickHouse 수준의 확장성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;요금&lt;/b&gt;: 데이터 볼륨&amp;middot;API 요청 수 기반 과금 (공개 가격표 존재)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;규모&lt;/b&gt;: PB급까지 지원 가능 (Tinybird 인프라에 의존)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 제품별 종합 비교표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 Druid Pinot ClickHouse StarRocks Doris DuckDB RisingWave Firebolt&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;저장 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자체&lt;/td&gt;
&lt;td&gt;자체&lt;/td&gt;
&lt;td&gt;자체&lt;/td&gt;
&lt;td&gt;자체/S3&lt;/td&gt;
&lt;td&gt;자체/S3&lt;/td&gt;
&lt;td&gt;외부 파일&lt;/td&gt;
&lt;td&gt;자체(S3)&lt;/td&gt;
&lt;td&gt;자체/S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실시간 수집&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ 최강&lt;/td&gt;
&lt;td&gt;✅ 최강&lt;/td&gt;
&lt;td&gt;△ 배치 지연&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ (밀리초~초)&lt;/td&gt;
&lt;td&gt;△ (배치 중심)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Upsert&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;△ 무거움&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ (MV 기반)&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;단일 테이블 쿼리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ 최강&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;✅ 최강급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;복잡 JOIN&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;△ 한계&lt;/td&gt;
&lt;td&gt;✅ 최강&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (소규모)&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;동시성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;최고&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;운영 복잡도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;없음 (완전SaaS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SQL 표준&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;부분&lt;/td&gt;
&lt;td&gt;부분&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;높음 (PG호환)&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Lakehouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;△&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ 최강&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;오픈소스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;관리형 서비스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Imply&lt;/td&gt;
&lt;td&gt;StarTree&lt;/td&gt;
&lt;td&gt;CH Cloud&lt;/td&gt;
&lt;td&gt;CelerData&lt;/td&gt;
&lt;td&gt;SelectDB&lt;/td&gt;
&lt;td&gt;MotherDuck&lt;/td&gt;
&lt;td&gt;RisingWave Cloud&lt;/td&gt;
&lt;td&gt;Firebolt Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;규모&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;PB급&lt;/td&gt;
&lt;td&gt;PB급&lt;/td&gt;
&lt;td&gt;PB급&lt;/td&gt;
&lt;td&gt;PB급&lt;/td&gt;
&lt;td&gt;TB~PB&lt;/td&gt;
&lt;td&gt;TB급&lt;/td&gt;
&lt;td&gt;TB~PB&lt;/td&gt;
&lt;td&gt;PB급&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 상황별 제품 선택 가이드&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.1 실시간 이벤트/스트리밍 분석 (append-only, 업데이트 없음)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;Apache Druid&lt;/b&gt; 또는 &lt;b&gt;Apache Pinot&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시 사용자 수만 명 이상의 사용자 직면 API &amp;rarr; &lt;b&gt;Pinot&lt;/b&gt; 우선&lt;/li&gt;
&lt;li&gt;내부 대시보드, 시계열 이벤트 분석 &amp;rarr; &lt;b&gt;Druid&lt;/b&gt; 또는 &lt;b&gt;ClickHouse&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.2 로그/메트릭 분석, 애드혹 쿼리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;ClickHouse&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 테이블 구조, 빠른 도입, 비용 효율 중요 시&lt;/li&gt;
&lt;li&gt;Cloudflare, Discord, Spotify 사례&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.3 복잡한 데이터 웨어하우스 쿼리 (다중 JOIN, Star Schema)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;StarRocks&lt;/b&gt; 또는 &lt;b&gt;Apache Doris&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 MySQL 스택 &amp;rarr; MySQL 호환 덕에 마이그레이션 수월&lt;/li&gt;
&lt;li&gt;Lakehouse 통합 필요 시 StarRocks Shared-Data 모드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.4 실시간 가변 데이터 (CDC, Upsert 필수)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;StarRocks&lt;/b&gt; (Primary Key) 또는 &lt;b&gt;Apache Pinot&lt;/b&gt; (Upsert)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDC(Change Data Capture) 파이프라인 &amp;rarr; StarRocks 우세&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.5 소규모 팀, 빠른 PoC, 로컬 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;DuckDB&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 없이 Python 노트북에서 즉시 분석&lt;/li&gt;
&lt;li&gt;S3/로컬 Parquet 파일 직접 쿼리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.6 스트림 처리 + 분석 통합 (Flink 대체)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;RisingWave&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실시간 집계 Materialized View + SQL 쿼리 단일 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.7 완전 서버리스, 운영 부담 제로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추천&lt;/b&gt;: &lt;b&gt;ClickHouse Cloud&lt;/b&gt; 또는 &lt;b&gt;Firebolt&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 확장성 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 확장 방식 컴퓨팅/스토리지 분리 최대 규모&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Druid&lt;/td&gt;
&lt;td&gt;컴포넌트별 수평 확장&lt;/td&gt;
&lt;td&gt;부분 (Deep Storage)&lt;/td&gt;
&lt;td&gt;수 PB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinot&lt;/td&gt;
&lt;td&gt;컴포넌트별 수평 확장&lt;/td&gt;
&lt;td&gt;부분&lt;/td&gt;
&lt;td&gt;수 PB (LinkedIn 1조+ 행)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClickHouse&lt;/td&gt;
&lt;td&gt;샤딩+리플리케이션&lt;/td&gt;
&lt;td&gt;신규 SharedCatalog(2025)&lt;/td&gt;
&lt;td&gt;수 PB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StarRocks&lt;/td&gt;
&lt;td&gt;Shared-Data 모드&lt;/td&gt;
&lt;td&gt;✅ 완전 분리 가능&lt;/td&gt;
&lt;td&gt;수 PB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Doris&lt;/td&gt;
&lt;td&gt;수평 확장&lt;/td&gt;
&lt;td&gt;✅ 지원&lt;/td&gt;
&lt;td&gt;수 PB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;단일 노드 (MotherDuck은 클라우드)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;수 TB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RisingWave&lt;/td&gt;
&lt;td&gt;수평 확장&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;수 TB~PB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 최신 트렌드 (2025~2026)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.1 Lakehouse 아키텍처 표준화&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Apache Iceberg가 사실상 표준&lt;/b&gt; 오픈 테이블 포맷으로 자리잡음&lt;/li&gt;
&lt;li&gt;모든 주요 OLAP DB(StarRocks, Doris, Druid, Pinot, ClickHouse)가 Iceberg External Catalog 지원&lt;/li&gt;
&lt;li&gt;벤더 락인 탈피, 동일 데이터를 여러 엔진에서 쿼리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.2 벡터 검색 통합 (AI/RAG 지원)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClickHouse: HNSW 벡터 인덱스 프로덕션 지원 (v25.8)&lt;/li&gt;
&lt;li&gt;Apache Pinot: HNSW 벡터 검색 내장&lt;/li&gt;
&lt;li&gt;StarRocks, Doris: 벡터 검색 기능 추가 중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의미&lt;/b&gt;: OLAP DB가 별도 벡터 DB(Milvus, Weaviate) 없이 AI 파이프라인 직접 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.3 임베디드 OLAP 카테고리 부상&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DuckDB 생태계 확산: chDB(ClickHouse), GlareDB, SlateDB&lt;/li&gt;
&lt;li&gt;pg_duckdb: PostgreSQL 내 DuckDB 엔진 임베딩&lt;/li&gt;
&lt;li&gt;서버 없이 애플리케이션 내 분석 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.4 컴퓨팅-스토리지 분리 (Cloud Native)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;StarRocks Shared-Data, ClickHouse SharedCatalog(2025)&lt;/li&gt;
&lt;li&gt;스토리지 비용 절감 + 컴퓨팅 탄력적 스케일링&lt;/li&gt;
&lt;li&gt;S3 기반 레이크하우스 + OLAP 엔진 패턴 일반화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.5 AI 통합 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자연어 쿼리(Text-to-SQL) 기능 OLAP DB에 직접 탑재 추세&lt;/li&gt;
&lt;li&gt;예측 분석, 이상 탐지 ML 모델 인라인 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11.6 스트리밍 DB 성장&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RisingWave, Materialize 등 스트리밍 DB: Flink + OLAP DB를 단일 시스템으로 대체&lt;/li&gt;
&lt;li&gt;실시간 Materialized View + ACID + PostgreSQL 호환 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 상세 벤치마크 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.1 벤치마크 유형별 특성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벤치마크 측정 내용 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ClickBench&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;단일 대형 테이블(웹 분석 로그) 집계 쿼리 43개&lt;/td&gt;
&lt;td&gt;ClickHouse 주도, 단순 풀스캔 강세 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;TPC-H&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;복잡한 다중 JOIN 포함 22개 쿼리&lt;/td&gt;
&lt;td&gt;데이터 웨어하우스 표준, JOIN 능력 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SSB (Star Schema Benchmark)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스타 스키마 기반 집계 쿼리&lt;/td&gt;
&lt;td&gt;실무 DW 패턴에 가까운 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.2 ClickBench 결과 (단일 테이블)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClickHouse&lt;/b&gt; 압도적 1위 &amp;mdash; 단순 집계 풀스캔 최강, 타 DB 대비 10~100배 빠른 쿼리 존재&lt;/li&gt;
&lt;li&gt;Druid는 ClickBench 기준 ClickHouse 대비 3~8배 느림&lt;/li&gt;
&lt;li&gt;Pinot은 ClickBench 직접 비교 데이터 제한적 (설계 목적 자체가 다름)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.3 TPC-H / SSB 결과 (다중 JOIN)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClickHouse, Apache Druid는 TPC-H 전체 쿼리 셋 완료 불가&lt;/b&gt; (JOIN 한계)&lt;/li&gt;
&lt;li&gt;StarRocks가 SSB flat table 기준 ClickHouse 대비 &lt;b&gt;1.87배 빠름&lt;/b&gt; (CBO 기반 JOIN 최적화)&lt;/li&gt;
&lt;li&gt;StarRocks vs Druid: SSB 기준 StarRocks가 &lt;b&gt;8.9배 빠름&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;복잡한 스타 스키마 쿼리에서 StarRocks &amp;gt; ClickHouse &amp;gt; Druid 순&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12.4 워크로드 유형별 최강자 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워크로드 1위 이유&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;단일 테이블 풀스캔 집계&lt;/td&gt;
&lt;td&gt;ClickHouse&lt;/td&gt;
&lt;td&gt;벡터화 실행 + 극한 압축&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복잡 다중 JOIN&lt;/td&gt;
&lt;td&gt;StarRocks&lt;/td&gt;
&lt;td&gt;MPP + CBO 조인 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;초저지연 실시간 수집 쿼리&lt;/td&gt;
&lt;td&gt;Pinot / Druid&lt;/td&gt;
&lt;td&gt;밀리초급 수집-즉시-쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소규모 로컬 분석&lt;/td&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;단일 노드 벡터화 엔진&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;16. 실제 도입 사례 (Case Study)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.1 LinkedIn &amp;mdash; Apache Pinot 창시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;배경&lt;/b&gt;: 수억 명 사용자의 &quot;Who viewed my profile&quot; 등 실시간 분석 기능 제공 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택 이유&lt;/b&gt;: 초고동시성(수십만 QPS) + 밀리초 응답 + 실시간 스트리밍 수집&lt;/li&gt;
&lt;li&gt;&lt;b&gt;규모&lt;/b&gt;: 1조 행 이상, 수백 노드 클러스터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과&lt;/b&gt;: Pinot을 직접 개발 &amp;rarr; Apache 오픈소스 기여&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.2 Uber &amp;mdash; Pinot + Druid 복합 운영&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;배경&lt;/b&gt;: 실시간 운전자/승객 지표, 재무 대시보드, 이상 탐지 등 다양한 분석 수요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조&lt;/b&gt;: Druid(시계열 이벤트) + Pinot(사용자 직면 API) 병행 운영&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최근 변화&lt;/b&gt;: Presto 기반 프록시 &amp;rarr; Pinot Multi-Stage Engine Lite Mode로 마이그레이션하여 JOIN 성능 개선&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Upsert 활용&lt;/b&gt;: 재무 대시보드&amp;middot;리스크 모니터링에 실시간 Upsert 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.3 Cloudflare &amp;mdash; ClickHouse&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;배경&lt;/b&gt;: 초당 수백만 DNS/HTTP 요청 로그 실시간 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택 이유&lt;/b&gt;: 단일 테이블 집계 쿼리 속도, 높은 압축률, 운영 단순성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마이그레이션&lt;/b&gt;: 자체 관리 ClickHouse 클러스터 &amp;rarr; ClickHouse Enterprise(Alibaba Cloud 관리형) 전환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결과&lt;/b&gt;: 연간 컴퓨팅&amp;middot;스토리지 비용 &lt;b&gt;40% 이상 절감&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.4 PostHog &amp;mdash; ClickHouse&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;배경&lt;/b&gt;: 오픈소스 제품 분석 플랫폼, 수억 이벤트 저장 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;선택 이유&lt;/b&gt;: 오픈소스, 뛰어난 압축, 이벤트 분석 특화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조&lt;/b&gt;: ClickHouse를 &quot;이벤트 맨션&quot;으로 표현 &amp;mdash; 모든 이벤트 데이터의 단일 저장소&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.5 Rokt &amp;mdash; ClickHouse (Pinot, Druid, StarRocks 검토 후 선택)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;평가 과정&lt;/b&gt;: Apache Pinot, Druid, Citus Data, StarRocks, Snowflake 모두 검토&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종 선택&lt;/b&gt;: ClickHouse &amp;mdash; 단순성, 쿼리 속도, 비용 효율 종합 평가 우위&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.6 Demandbase &amp;mdash; CelerData(StarRocks)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;전환 결과&lt;/b&gt;: StarRocks 기반 CelerData Cloud로 전환 후 &lt;b&gt;스토리지 비용 90% 절감&lt;/b&gt;, 하드웨어 사용량 60% 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이유&lt;/b&gt;: 복잡한 B2B 분석 JOIN 쿼리 + CDC 기반 데이터 변경 처리 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.7 DuckDB / MotherDuck &amp;mdash; 소규모 팀 &amp;middot; 임베디드 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Dexibit (박물관 분석)&lt;/b&gt;: MotherDuck으로 전통 데이터 웨어하우스 대체. 고객용 대화형 대시보드 구축, 동일 SQL로 로컬&amp;harr;클라우드 원활하게 전환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Definite&lt;/b&gt;: DuckDB 기반 아키텍처 전환 후 &lt;b&gt;인프라 비용 70% 절감&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gardyn (IoT 분석)&lt;/b&gt;: MotherDuck 기반 스택이 기존 대안 대비 &lt;b&gt;10배 저렴&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Finqore (핀테크)&lt;/b&gt;: 8시간 걸리던 데이터 파이프라인을 &lt;b&gt;8분&lt;/b&gt;으로 단축 &amp;mdash; AI 에이전트 실시간 처리 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;미공개 팀&lt;/b&gt;: Snowflake BI 비용 &lt;b&gt;79% 절감&lt;/b&gt; (DuckDB 스마트 캐싱 레이어 활용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13.8 RisingWave &amp;mdash; 스트리밍 DB 도입 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SHOPLINE (커머스 플랫폼)&lt;/b&gt;: 실시간 주문 분석 고객 직면 기능 구현. RisingWave를 스트리밍+히스토리컬 SQL 통합 레이어로 채택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;글로벌 금융기관 (수십 조 달러 규모)&lt;/b&gt;: 미션크리티컬 내부 워크로드에 RisingWave 도입, 전사 확산 중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;금융 브로커 리더&lt;/b&gt;: 사기 탐지 피처 스토어 구축에 RisingWave 핵심 컴포넌트로 활용 &amp;mdash; 데이터 파이프라인 단순화 및 신뢰성 향상&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전체 규모&lt;/b&gt;: 1,000개 이상 기업&amp;middot;스타트업 도입 (2025 기준)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;17. 운영 비용 비교 (TCO)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.1 비용 구성 요소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용 유형 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인프라 비용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컴퓨팅(EC2/VM) + 스토리지(EBS/S3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;관리형 서비스 요금&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;클라우드 서비스 마크업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;People TCO&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;엔지니어링 유지보수&amp;middot;온콜 인건비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 전송(Egress)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;리전 간&amp;middot;인터넷 데이터 전송 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.2 관리형 서비스 요금 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 기반 제품 가격 모델 비고&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ClickHouse Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;ClickHouse&lt;/td&gt;
&lt;td&gt;컴퓨팅 초당 과금 + 스토리지 $35~50/TB&lt;/td&gt;
&lt;td&gt;개발: $1~193/월, 프로덕션: $500~$100,000/월&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;http://Altinity.Cloud&quot;&gt;&lt;b&gt;Altinity.Cloud&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;ClickHouse (100% 오픈소스)&lt;/td&gt;
&lt;td&gt;BYOC (AWS/GCP/Azure)&lt;/td&gt;
&lt;td&gt;프로프라이어터리 수정 없음, 엔터프라이즈 SLA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CelerData Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;StarRocks&lt;/td&gt;
&lt;td&gt;컴퓨팅+스토리지 분리 과금&lt;/td&gt;
&lt;td&gt;Demandbase 사례: 스토리지 90% 절감&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;StarTree Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Pinot&lt;/td&gt;
&lt;td&gt;컴퓨팅+스토리지 분리 과금&lt;/td&gt;
&lt;td&gt;LinkedIn 팀 주도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Imply Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Druid&lt;/td&gt;
&lt;td&gt;컴퓨팅+스토리지 분리 과금&lt;/td&gt;
&lt;td&gt;Druid 원조 팀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SelectDB Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Doris&lt;/td&gt;
&lt;td&gt;컴퓨팅+스토리지 분리 과금&lt;/td&gt;
&lt;td&gt;중국 클라우드 친화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MotherDuck&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;서버리스, 쿼리 기반 과금&lt;/td&gt;
&lt;td&gt;소규모 팀 최적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RisingWave Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RisingWave&lt;/td&gt;
&lt;td&gt;RWU(1 vCPU 또는 4GB RAM) 단위 과금 + 스토리지 GB/월&lt;/td&gt;
&lt;td&gt;완전 관리형 또는 BYOC, Pay-as-you-go/연간 계약 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Firebolt&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Firebolt (완전 상용)&lt;/td&gt;
&lt;td&gt;AWS/GCP 기반 SaaS, 영업 문의 (공개 가격 없음)&lt;/td&gt;
&lt;td&gt;오픈소스 없음, SOC2 Type II, 엔터프라이즈 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14.3 자체 운영 vs 관리형 TCO 판단 기준&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;People TCO&lt;/b&gt;: 엔지니어링 유지보수&amp;middot;온콜 인건비 월 &lt;b&gt;$1,600~$4,800&lt;/b&gt; 추가 발생 &amp;mdash; 소규모 팀에서는 관리형이 유리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClickHouse Cloud vs Snowflake&lt;/b&gt;: ClickHouse Cloud 기준 Snowflake 대비 약 &lt;b&gt;4배 낮은 TCO&lt;/b&gt; (극한 압축률 덕분)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자체 운영 권장 조건&lt;/b&gt;: 전담 DBA/인프라 팀 존재, 페타바이트급 대규모, 커스텀 하드웨어 최적화 필요 시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관리형 권장 조건&lt;/b&gt;: 소규모 팀, 빠른 시작, 버스티한 워크로드, 인프라 운영 부담 최소화 필요 시&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;18. 데이터 수집(Ingestion) 파이프라인 패턴&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.1 Kafka 직접 수집 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 수집 방식 지연 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Pinot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Consuming Segments&lt;/td&gt;
&lt;td&gt;&lt;b&gt;밀리초&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;행 단위 수집, 즉시 쿼리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Druid&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka Indexing Service&lt;/td&gt;
&lt;td&gt;&lt;b&gt;밀리초~초&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;세그먼트 단위, 수집 즉시 쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ClickHouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka Table Engine + MV&lt;/td&gt;
&lt;td&gt;&lt;b&gt;초~분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;마이크로배치, 일정 지연 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;StarRocks&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka Routine Load&lt;/td&gt;
&lt;td&gt;&lt;b&gt;초&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;안정적 초 단위 수집&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Doris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka Routine Load&lt;/td&gt;
&lt;td&gt;&lt;b&gt;초&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;StarRocks와 유사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RisingWave&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka/Kinesis Native Connector + Materialized View&lt;/td&gt;
&lt;td&gt;&lt;b&gt;밀리초~초&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스트림 처리 + 집계 동시 수행, MV 즉시 쿼리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;해당 없음 (Kafka 직접 수집 불가)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;파일/S3/Iceberg 직접 읽기 전용 &amp;mdash; 스트리밍 수집 미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Firebolt&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kafka Connect, S3 External Tables&lt;/td&gt;
&lt;td&gt;&lt;b&gt;초~분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;배치 중심, 완전 서버리스 자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.2 CDC (Change Data Capture) 파이프라인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;표준 아키텍처&lt;/b&gt;: Source DB &amp;rarr; Debezium &amp;rarr; Kafka &amp;rarr; OLAP DB&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClickHouse CDC&lt;/b&gt;: Debezium &amp;rarr; Kafka &amp;rarr; ClickHouse Kafka Connect &amp;rarr; Materialized View로 실시간 반영. 2024년 &quot;Lightweight Updates&quot; 도입으로 CDC 실용성 획기적 개선 (100초 &amp;rarr; 60ms, 1,600배 향상)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;StarRocks CDC&lt;/b&gt;: Primary Key 테이블의 Upsert 기능으로 CDC 스트림 네이티브 처리. 실시간 변경 반영에 최적화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pinot CDC&lt;/b&gt;: Upsert 테이블로 CDC 지원. Primary Key 기반 실시간 업데이트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Druid CDC&lt;/b&gt;: &lt;b&gt;미지원&lt;/b&gt; &amp;mdash; append-only 설계, CDC 필요 시 다른 제품 고려 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.3 배치 수집 패턴&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;S3/HDFS &amp;rarr; OLAP&lt;/b&gt;: StarRocks, Doris, ClickHouse 모두 S3 직접 로드 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spark &amp;rarr; OLAP&lt;/b&gt;: StarRocks Spark Connector, ClickHouse Spark Connector 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flink &amp;rarr; OLAP&lt;/b&gt;: 실시간 집계 후 OLAP으로 적재 (StarRocks Flink Connector 공식 지원)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15.4 하이브리드 Lambda/Kappa 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Lambda&lt;/b&gt;: 배치(Hadoop/Spark) + 실시간(Kafka&amp;rarr;OLAP) 병행 &amp;rarr; 복잡도 높음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kappa&lt;/b&gt;: Kafka 단일 스트림으로 배치+실시간 통합 &amp;rarr; RisingWave, StarRocks가 이 패턴 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트렌드&lt;/b&gt;: Kappa 아키텍처 + 오픈 테이블 포맷(Iceberg)으로 통합 단순화 추세&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;19. 보안 및 거버넌스&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16.1 컴플라이언스 인증 현황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 SOC2 ISO 27001 HIPAA GDPR 비고&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ClickHouse Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ Type II&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;U.S. DPF 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;StarTree (Pinot)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;관리형 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CelerData (StarRocks)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;관리형 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Imply (Druid)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;관리형 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MotherDuck (DuckDB)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ Type II&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;서비스 계정 토큰 기반 접근 제어, Business Plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RisingWave Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;완전 관리형 또는 BYOC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Firebolt&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ Type II&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;RBAC, MFA(Okta/Auth0), TLS+AES-256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;오픈소스 자체 운영&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;❌ (직접 구현 필요)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;기능 제공&lt;/td&gt;
&lt;td&gt;GDPR 삭제권 등 직접 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16.2 접근 제어 (RBAC)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ClickHouse&lt;/b&gt;: 세분화된 RBAC &amp;mdash; 데이터베이스&amp;middot;테이블&amp;middot;시스템 리소스 수준 SELECT/INSERT/CREATE 권한 개별 부여/회수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;StarRocks&lt;/b&gt;: RBAC 지원, 행/열 수준 보안(Row-level Security, Column Masking) 엔터프라이즈 기능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Pinot&lt;/b&gt;: 테이블 수준 접근 제어, 멀티 테넌시 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Druid&lt;/b&gt;: 기본 인증&amp;middot;인가 + Ranger 플러그인으로 세분화 제어 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Firebolt&lt;/b&gt;: 계층적 RBAC, MFA(Okta/Auth0 통합), TLS 전송 암호화 + AES-256 저장 암호화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MotherDuck(DuckDB)&lt;/b&gt;: 서비스 계정 토큰 기반 읽기/쓰기 분리 접근 제어, 리드 스케일링 레플리카&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RisingWave Cloud&lt;/b&gt;: PostgreSQL 호환 권한 모델 (GRANT/REVOKE), 멀티 테넌시 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16.3 GDPR 대응 &amp;mdash; 삭제권(Right to Erasure)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;StarRocks / Doris&lt;/b&gt;: Primary Key 테이블의 DELETE 연산으로 특정 사용자 데이터 효율적 삭제 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClickHouse&lt;/b&gt;: ALTER TABLE DELETE (Mutation) &amp;mdash; 무거우나 가능. 2024년 Lightweight Delete로 개선&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Druid&lt;/b&gt;: 세그먼트 단위 삭제만 가능, 행 단위 삭제 어려움 &amp;rarr; GDPR 대응 비적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Pinot&lt;/b&gt;: Upsert/Delete 지원으로 행 단위 삭제 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16.4 데이터 마스킹 및 감사&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClickHouse: 컬럼 마스킹 정책, 쿼리 로그 기반 감사&lt;/li&gt;
&lt;li&gt;StarRocks: Dynamic Column Masking (엔터프라이즈), Audit Log Plugin&lt;/li&gt;
&lt;li&gt;공통: TLS/SSL 암호화 전송, 저장 데이터 암호화(AES-256) 클라우드 관리형에서 기본 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;20. Hadoop 생태계 변화와 이전 전략&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17.1 Hadoop 쇠퇴 배경&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;2019년&lt;/b&gt;: Cloudera + Hortonworks 합병 &amp;rarr; Hadoop 상용 에코시스템 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;근본 한계&lt;/b&gt;: 온디맨드 쿼리 부재, 동적 스키마 미지원, 클라우드 네이티브 기술과 호환성 부족&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Impala/Hive의 현재&lt;/b&gt;: Hadoop 의존성으로 클라우드 네이티브 전환 시 입지 약화. Impala는 Hadoop 없이 독립 운영 사실상 불가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시장 규모&lt;/b&gt;: Hadoop 시장은 2025년 약 $8B 규모로 유지되나, 신규 도입은 크게 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17.2 클라우드 네이티브 전환 시 대안 선택지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 역할 클라우드 네이티브 대안&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hive (배치 SQL)&lt;/td&gt;
&lt;td&gt;Trino / Spark SQL + Iceberg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Impala (대화형 SQL)&lt;/td&gt;
&lt;td&gt;Trino, Presto, StarRocks(External Catalog)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBase (실시간 KV)&lt;/td&gt;
&lt;td&gt;Apache Cassandra, DynamoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HDFS (분산 스토리지)&lt;/td&gt;
&lt;td&gt;S3, GCS, Azure Blob + Iceberg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MapReduce (배치 처리)&lt;/td&gt;
&lt;td&gt;Apache Spark, Flink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Druid on Hadoop&lt;/td&gt;
&lt;td&gt;Druid on Kubernetes + S3 Deep Storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17.3 Hadoop &amp;rarr; 클라우드 네이티브 전환 효과&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 레이턴시 및 동시성 &lt;b&gt;30~70% 개선&lt;/b&gt; (실측 사례)&lt;/li&gt;
&lt;li&gt;인프라 운영 비용 절감 (온프레미스 서버 &amp;rarr; 클라우드 탄력적 과금)&lt;/li&gt;
&lt;li&gt;Apache Iceberg + Trino/StarRocks 조합이 현재 가장 일반적인 마이그레이션 패턴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Snowflake/AWS/GCP 네이티브 서비스&lt;/b&gt;로의 완전 이관도 활발 (특히 Hive &amp;rarr; Snowflake)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;21. HTAP (Hybrid Transactional/Analytical Processing) 동향&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18.1 HTAP 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OLTP(트랜잭션 처리)와 OLAP(분석 처리)를 단일 시스템에서 제공하는 아키텍처. 실시간 의사결정(사기 탐지, 가격 최적화, 개인화)에 필요한 최신 데이터 즉시 분석 가능.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18.2 주요 HTAP 제품&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TiDB (PingCAP)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;아키텍처&lt;/b&gt;: TiKV (Row 스토어, OLTP) + TiFlash (Column 스토어, OLAP) 듀얼 엔진&lt;/li&gt;
&lt;li&gt;TiKV는 CNCF 졸업 프로젝트, 완전 오픈소스&lt;/li&gt;
&lt;li&gt;같은 데이터를 Row 형태(OLTP용)와 Column 형태(OLAP용)로 동시 유지&lt;/li&gt;
&lt;li&gt;실시간 분석 + 트랜잭션을 단일 SQL로 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SingleStore (구 MemSQL)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;아키텍처&lt;/b&gt;: 각 노드에 인메모리 Row 스토어 + Column 스토어 + 디스크 파일 혼용&lt;/li&gt;
&lt;li&gt;MySQL 호환, 분산 아키텍처&lt;/li&gt;
&lt;li&gt;실시간 수집 + 분석 쿼리 동시 처리 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;StarRocks / Apache Doris의 HTAP 접근&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순수 HTAP는 아니나 Primary Key + 실시간 수집 + 물화 뷰로 유사 사용 사례 커버&lt;/li&gt;
&lt;li&gt;10,000+ QPS + 신선한 데이터 + 고동시성 BI 쿼리 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18.3 시장 현황 (2025)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTAP는 이론적으로 매력적이나 &lt;b&gt;실제 도입은 제한적&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;클라우드 DW(Snowflake, BigQuery)가 분석 시장의 주도권을 가져가면서 순수 HTAP 포지셔닝이 약화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;현실적 트렌드&lt;/b&gt;: OLTP(PostgreSQL/MySQL) + CDC + OLAP DB 조합이 HTAP 단일 시스템보다 더 널리 사용됨&lt;/li&gt;
&lt;li&gt;사기 탐지, 실시간 추천 등 극한 레이턴시 요구 시에만 TiDB/SingleStore 선택 유효&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;22. 오픈소스 vs 상용 관리형 서비스 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19.1 기능 차이 (ClickHouse 기준 대표 사례)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 오픈소스 자체 운영 ClickHouse Cloud (관리형)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;코어 쿼리 엔진&lt;/td&gt;
&lt;td&gt;✅ 동일&lt;/td&gt;
&lt;td&gt;✅ 동일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동 스케일링&lt;/td&gt;
&lt;td&gt;❌ 수동&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동 리플리케이션/페일오버&lt;/td&gt;
&lt;td&gt;❌ 수동 설정&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동 백업&lt;/td&gt;
&lt;td&gt;❌ 수동&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SharedMergeTree (컴퓨팅-스토리지 분리)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Cloud 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightweight UPDATE&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Cloud 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Role-based Access&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모니터링/대시보드&lt;/td&gt;
&lt;td&gt;❌ 외부 도구 필요&lt;/td&gt;
&lt;td&gt;✅ 내장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보안 인증 (SOC2 등)&lt;/td&gt;
&lt;td&gt;❌ 직접 구현&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19.2 제품별 오픈소스 vs 관리형 포지셔닝&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품 오픈소스 관리형 서비스 오픈코어 여부&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ClickHouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;ClickHouse Cloud, &lt;a href=&quot;http://Altinity.Cloud&quot;&gt;Altinity.Cloud&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;일부 Cloud 전용 기능 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Pinot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;StarTree Cloud&lt;/td&gt;
&lt;td&gt;완전 오픈소스 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Druid&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;Imply Cloud&lt;/td&gt;
&lt;td&gt;완전 오픈소스 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;StarRocks&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;CelerData Cloud&lt;/td&gt;
&lt;td&gt;오픈소스 완전 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Doris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;SelectDB Cloud&lt;/td&gt;
&lt;td&gt;완전 오픈소스 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MotherDuck&lt;/td&gt;
&lt;td&gt;완전 오픈소스 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;RisingWave&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;RisingWave Cloud (AWS/GCP/Azure BYOC 포함)&lt;/td&gt;
&lt;td&gt;완전 오픈소스 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Firebolt&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;❌ 없음 (완전 상용)&lt;/td&gt;
&lt;td&gt;Firebolt Cloud (AWS/GCP)&lt;/td&gt;
&lt;td&gt;오픈소스 버전 없음 &amp;mdash; 관리형 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Tinybird&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;❌ 없음 (완전 상용)&lt;/td&gt;
&lt;td&gt;Tinybird Cloud (ClickHouse 기반)&lt;/td&gt;
&lt;td&gt;오픈소스 버전 없음 &amp;mdash; API 플랫폼 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19.3 자체 운영 권장 vs 관리형 권장 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자체 운영이 유리한 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전담 DBA&amp;middot;인프라 팀 보유&lt;/li&gt;
&lt;li&gt;페타바이트급 대규모 (관리형 비용이 자체보다 높아지는 시점)&lt;/li&gt;
&lt;li&gt;특수 하드웨어(고성능 NVMe, 대용량 RAM) 최적화 필요&lt;/li&gt;
&lt;li&gt;데이터 주권 규정으로 외부 클라우드 저장 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관리형이 유리한 경우&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소규모 팀(5인 이하 엔지니어링)&lt;/li&gt;
&lt;li&gt;빠른 프로덕션 출시 필요 (수 시간 내 클러스터 구성)&lt;/li&gt;
&lt;li&gt;버스티한 워크로드 (자동 스케일 업/다운)&lt;/li&gt;
&lt;li&gt;People TCO($1,600~$4,800/월) 대비 관리형 요금이 저렴한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;19.4 Altinity &amp;mdash; 100% 오픈소스 엔터프라이즈 대안&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ClickHouse 오픈소스를 그대로 사용 (프로프라이어터리 수정 없음)&lt;/li&gt;
&lt;li&gt;BYOC (AWS/GCP/Azure 고객 계정 내 배포)&lt;/li&gt;
&lt;li&gt;24/7 SLA, 핵심 ClickHouse 기여자 팀 운영&lt;/li&gt;
&lt;li&gt;오픈소스 통제권 + 관리형 편의성을 동시에 원하는 팀에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;  레퍼런스&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공식 문서 및 홈페이지&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://druid.apache.org/&quot;&gt;Apache Druid 공식 사이트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://druid.apache.org/docs/latest/design/&quot;&gt;Apache Druid 아키텍처 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pinot.apache.org/&quot;&gt;Apache Pinot 공식 사이트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pinot.apache.org/&quot;&gt;Apache Pinot 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/docs/academic_overview&quot;&gt;ClickHouse 아키텍처 개요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/pricing&quot;&gt;ClickHouse Cloud 요금&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/docs/cloud/security/compliance-overview&quot;&gt;ClickHouse Cloud 보안 컴플라이언스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://impala.apache.org/&quot;&gt;Apache Impala 공식 사이트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://duckdb.org/&quot;&gt;DuckDB 공식 사이트&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벤치마크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://benchmark.clickhouse.com/&quot;&gt;ClickBench &amp;mdash; 분석 DBMS 벤치마크 (ClickHouse)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ClickHouse/ClickBench&quot;&gt;ClickBench GitHub 저장소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.starrocks.io/blog/benchmark-test&quot;&gt;StarRocks vs ClickHouse, Apache Druid, Trino 벤치마크 (StarRocks 공식)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celerdata.com/blog/starrocks-queries-outperform-clickhouse-apache-druid-and-trino&quot;&gt;StarRocks vs ClickHouse 성능 비교 (CelerData)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tinybird.co/blog/clickhouse-vs-starrocks&quot;&gt;ClickHouse vs StarRocks 속도 비교 (Tinybird)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tinybird.co/blog/fastest-database-for-analytics&quot;&gt;2026 가장 빠른 분석 데이터베이스 비교 (Tinybird)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제품 비교 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://startree.ai/resources/a-tale-of-three-real-time-olap-databases/&quot;&gt;Pinot vs Druid vs ClickHouse 비교 (StarTree)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ksolves.com/blog/big-data/pinot-vs-druid-vs-clickhouse&quot;&gt;Pinot vs Druid vs ClickHouse 비교 (Ksolves)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://risingwave.com/blog/big-data-olap-systems-apache-pinot-vs-clickhouse-vs-druid/&quot;&gt;Pinot vs ClickHouse vs Druid (RisingWave)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://imply.io/blog/apache-druid-vs-clickhouse/&quot;&gt;ClickHouse vs Druid 비교 (Imply)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celerdata.com/blog/clickhouse-vs.-starrocks-a-detailed-comparison&quot;&gt;ClickHouse vs StarRocks 상세 비교 (CelerData)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://habr.com/en/articles/962912/&quot;&gt;StarRocks vs ClickHouse, Druid, Trino (Habr)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://risingwave.com/blog/top-real-time-olap-databases-in-2025/&quot;&gt;2025 상위 실시간 OLAP 데이터베이스 (RisingWave)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tinybird.co/blog/best-database-for-olap&quot;&gt;2026 OLAP 데이터베이스 현황 (Tinybird)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pracdata.io/p/state-of-open-source-read-time-olap-2025&quot;&gt;오픈소스 실시간 OLAP 시스템 현황 2025 (Pracdata)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://estuary.dev/blog/real-time-olap-databases/&quot;&gt;2026 상위 10 실시간 OLAP 데이터베이스 (Estuary)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아키텍처 및 기술 심층 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chaosgenius.io/blog/clickhouse-architecture/&quot;&gt;ClickHouse 아키텍처 101 (Chaos Genius)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celerdata.com/blog/understanding-clickhouse-benefits-and-limitations&quot;&gt;ClickHouse 장단점 이해 (CelerData)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/blog/clickhouse-2025-roundup&quot;&gt;ClickHouse 2025 연간 정리 (ClickHouse 공식 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.modern-datatools.com/tools/pinot&quot;&gt;Apache Pinot 리뷰 2026 (Modern DataTools)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.dremio.com/wiki/apache-impala/&quot;&gt;Apache Impala What is (Dremio)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://asia.communityovercode.org/sessions/olap-915180.html&quot;&gt;Impala 4.5 신기능 &amp;mdash; Iceberg, 성능 (Community Over Code Asia 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@bhagyarana80/duckdb-vs-the-world-a-2025-guide-to-modern-analytical-databases-703c686cfa4d&quot;&gt;DuckDB vs 세계: 2025 분석 DB 가이드 (Medium)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.influxdata.com/comparison/doris-vs-duckdb/&quot;&gt;Apache Doris vs DuckDB 비교 (InfluxData)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;도입 사례 (Case Studies)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/user-stories&quot;&gt;ClickHouse 사용자 스토리 모음 (ClickHouse 공식)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://posthog.com/blog/how-we-turned-clickhouse-into-our-eventmansion&quot;&gt;PostHog: ClickHouse를 이벤트 맨션으로 (PostHog 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.uber.com/us/en/blog/rebuilding-ubers-apache-pinot-query-architecture/&quot;&gt;Uber: Apache Pinot 쿼리 아키텍처 재구축 (Uber 엔지니어링)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.wiz.io/blog/clickhouse-and-wiz&quot;&gt;Cloudflare ClickHouse 활용 사례 (Wiz 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 비용 및 관리형 서비스&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tinybird.co/blog/self-hosted-clickhouse-cost&quot;&gt;ClickHouse 자체 호스팅 비용 (Tinybird)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://oneuptime.com/blog/post/2026-03-31-clickhouse-cloud-vs-open-source-comparison/view&quot;&gt;ClickHouse Cloud vs 오픈소스 기능 비교 (OneUptime)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.getorchestra.io/guides/clickhouse-enterprise-vs-open-source&quot;&gt;ClickHouse Enterprise vs 오픈소스 (Orchestra)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://altinity.com/blog/is-clickhouse-moving-away-from-open-source&quot;&gt;Altinity: ClickHouse 오픈소스에서 멀어지나? (Altinity 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tinybird.co/blog/best-cloud-managed-clickhouse&quot;&gt;관리형 ClickHouse 서비스 비교 2025 (Tinybird)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celerdata.com/clickhouse-alternatives-comparisons&quot;&gt;ClickHouse 대안 비교 (CelerData)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 수집 및 CDC&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://analytics.axxonet.com/blog/debezium-kafka-hop-cdc-pipelines&quot;&gt;Debezium + Kafka + ClickHouse CDC 파이프라인 (Axxonet)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/blog/clickhouse-postgresql-change-data-capture-cdc-part-2&quot;&gt;PostgreSQL &amp;rarr; ClickHouse CDC Part 2 (ClickHouse 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mux.com/blog/latency-and-throughput-tradeoffs-of-clickhouse-kafka-table-engine&quot;&gt;ClickHouse Kafka 레이턴시 최적화: 12s &amp;rarr; 2s (Mux 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/blog/change-data-capture-solution-clickhouse-streamkap-cdc&quot;&gt;Streamkap: ClickHouse용 CDC 솔루션 (ClickHouse 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보안 및 거버넌스&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/blog/clickhouse-cloud-is-now-soc-2-type-ii-compliant&quot;&gt;ClickHouse Cloud SOC 2 Type II 인증 (ClickHouse 블로그)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://clickhouse.com/docs/cloud/security/compliance-overview&quot;&gt;ClickHouse Cloud 컴플라이언스 개요 (ClickHouse 문서)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hadoop 생태계 및 마이그레이션&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kitrum.com/blog/why-its-time-to-move-on-from-hadoop/&quot;&gt;Hadoop에서 벗어나야 할 시기 (Kitrum)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@Jain.Parag/modernizing-data-platforms-migrating-hadoop-to-snowflake-and-aws-to-unlock-ai-and-analytics-with-67255eb41c74&quot;&gt;Hadoop &amp;rarr; Snowflake + Iceberg 마이그레이션 (Medium)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Apache_Impala&quot;&gt;Apache Impala Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTAP&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pingcap.com/blog/real-world-htap-a-look-at-tidb-and-singlestore-and-their-architectures/&quot;&gt;실제 HTAP: TiDB와 SingleStore 아키텍처 (PingCAP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/news/2025/06/htap-databases/&quot;&gt;HTAP 데이터베이스의 부상과 몰락? (InfoQ)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celerdata.com/glossary/hybrid-transactional-analytical-processing&quot;&gt;HTAP 이해 (CelerData)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.singlestore.com/blog/what-is-htap/&quot;&gt;HTAP 입문 가이드 (SingleStore)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부/데이터</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/751</guid>
      <comments>https://brownbears.tistory.com/751#entry751comment</comments>
      <pubDate>Wed, 29 Apr 2026 22:43:55 +0900</pubDate>
    </item>
    <item>
      <title>SQL 안티패턴 감지</title>
      <link>https://brownbears.tistory.com/750</link>
      <description>&lt;h1&gt;목표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 SQL 쿼리를 실행 전/후로 분석하여 안티패턴을 감지하고 튜닝 포인트를 제공합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Dry-run&lt;/b&gt; &amp;mdash; 실제 실행 없이 쿼리 구조를 정적 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;필수 규칙 강제&lt;/b&gt; &amp;mdash; Critical 안티패턴은 차단 또는 강한 경고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 전 튜닝 포인트&lt;/b&gt; &amp;mdash; 정적 분석 / EXPLAIN 기반 개선 제안&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 후 튜닝 포인트&lt;/b&gt; &amp;mdash; 실행 통계 기반 병목 진단&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;참고 도구 분석&lt;/h1&gt;
&lt;p&gt;도구 안티패턴 수 플랫폼 접근 방식&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery Anti-Pattern Recognition&lt;/td&gt;
&lt;td&gt;11개&lt;/td&gt;
&lt;td&gt;BigQuery 전용&lt;/td&gt;
&lt;td&gt;AST 정적 분석 (ZetaSQL 파서)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlcheck&lt;/td&gt;
&lt;td&gt;29개&lt;/td&gt;
&lt;td&gt;범용 RDBMS&lt;/td&gt;
&lt;td&gt;CLI 정적 분석 (C++)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AltimateAI / altimate-code&lt;/td&gt;
&lt;td&gt;19개&lt;/td&gt;
&lt;td&gt;10개 웨어하우스 (Snowflake&amp;middot;BQ&amp;middot;Databricks 등)&lt;/td&gt;
&lt;td&gt;AI 기반 + 정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlglot&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;31개 방언 (Trino&amp;middot;Databricks&amp;middot;Snowflake 등)&lt;/td&gt;
&lt;td&gt;Python 파서&amp;middot;트랜스파일러 (자체 구현 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;BigQuery Anti-Pattern Recognition&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글이 공식 오픈소스로 제공한 도구입니다. ZetaSQL 파서로 AST를 분석하고, 실제 쿼리 실행 없이 안티패턴을 탐지합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;감지 안티패턴 11개&lt;/h2&gt;
&lt;p&gt;# 이름 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;SimpleSelectStar&lt;/td&gt;
&lt;td&gt;SELECT * &amp;mdash; 불필요한 전체 컬럼 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;SemiJoinWithoutAggregation&lt;/td&gt;
&lt;td&gt;IN 필터 서브쿼리에서 DISTINCT 누락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;MultipleCTEReferences&lt;/td&gt;
&lt;td&gt;동일 CTE를 2회 이상 참조 (재연산 발생)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;OrderByWithoutLimit&lt;/td&gt;
&lt;td&gt;LIMIT 없는 ORDER BY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;RegexpContainsUsage&lt;/td&gt;
&lt;td&gt;REGEXP_CONTAINS 대신 LIKE 사용 권고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;LatestRecordWithAnalyticFun&lt;/td&gt;
&lt;td&gt;ROW_NUMBER()로 최신 레코드 필터링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;DynamicPredicate&lt;/td&gt;
&lt;td&gt;서브쿼리 필터 &amp;rarr; 정적 술어로 변환 권고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;WhereOrder&lt;/td&gt;
&lt;td&gt;WHERE 절 필터 순서 &amp;mdash; 선택도 높은 조건 우선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;JoinOrder&lt;/td&gt;
&lt;td&gt;테이블 크기 기반 JOIN 순서 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;MissingDropStatement&lt;/td&gt;
&lt;td&gt;TEMP 테이블 생성 후 DROP 누락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;DroppedPersistentTable&lt;/td&gt;
&lt;td&gt;스크립트 끝에서 영구 테이블 삭제 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# 사전 조건: JDK 11+, Maven, Docker, gcloud CLI
gcloud auth application-default login
git clone &amp;lt;https://github.com/GoogleCloudPlatform/bigquery-antipattern-recognition.git&amp;gt;
cd bigquery-antipattern-recognition

# Docker 이미지 빌드
mvn clean package jib:dockerBuild -DskipTests

# 또는 JAR 직접 다운로드
wget &amp;lt;https://github.com/GoogleCloudPlatform/bigquery-antipattern-recognition/releases/download/v1.0.0/bigquery-antipattern-recognition.jar&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;입력 옵션 (Input Flags)&lt;/h2&gt;
&lt;p&gt;플래그 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;--query &quot;SELECT ...&quot;&lt;/td&gt;
&lt;td&gt;CLI에서 SQL 문자열 직접 입력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--read_from_info_schema&lt;/td&gt;
&lt;td&gt;INFORMATION_&lt;a href=&quot;http://SCHEMA.JOBS&quot;&gt;SCHEMA.JOBS&lt;/a&gt;에서 쿼리 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--read_from_info_schema_days N&lt;/td&gt;
&lt;td&gt;읽을 기간 (기본값: 1일)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--info_schema_project &amp;lt;project&amp;gt;&lt;/td&gt;
&lt;td&gt;분석 대상 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--info_schema_region us&lt;/td&gt;
&lt;td&gt;리전 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--read_from_info_schema_start_time &quot;timestamp&quot;&lt;/td&gt;
&lt;td&gt;시작 시간 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--read_from_info_schema_end_time &quot;timestamp&quot;&lt;/td&gt;
&lt;td&gt;종료 시간 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--info_schema_top_n_percentage_of_jobs N&lt;/td&gt;
&lt;td&gt;상위 슬롯 소비 비율 (0~1, 예: 0.1 = 상위 10%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--input_bq_table project.dataset.table&lt;/td&gt;
&lt;td&gt;BigQuery 테이블에서 읽기 (컬럼: id, query)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--input_file_path /path/to/file.sql&lt;/td&gt;
&lt;td&gt;로컬 또는 GCS 파일에서 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--input_folder_path /path/to/folder&lt;/td&gt;
&lt;td&gt;폴더 내 전체 .sql 파일 파싱&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--input_csv_file_path /path/to/file.csv&lt;/td&gt;
&lt;td&gt;CSV 파일에서 읽기 (컬럼: id, query)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출력 옵션 (Output Flags)&lt;/h2&gt;
&lt;p&gt;플래그 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;--output_file_path /path/to/output.csv&lt;/td&gt;
&lt;td&gt;CSV 파일로 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--output_table &quot;project.dataset.table&quot;&lt;/td&gt;
&lt;td&gt;BigQuery 테이블에 결과 저장 (사전 생성 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--rewrite_sql&lt;/td&gt;
&lt;td&gt;Vertex AI로 최적화된 SQL 자동 재작성 (실험 기능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--processing_project_id &amp;lt;project&amp;gt;&lt;/td&gt;
&lt;td&gt;INFORMATION_SCHEMA 조회 및 결과 저장 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인라인 쿼리 분석 (Docker)&lt;/h3&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;docker run -i bigquery-antipattern-recognition \\
  --query &quot;SELECT * FROM \\`project.dataset.orders\\` WHERE YEAR(created_at) = 2024&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# 출력 예시
SimpleSelectStar: SELECT * at line 1. All columns on table are being selected.
Non-sargable predicate: YEAR(created_at) at line 1 disables partition pruning.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인라인 쿼리 분석 (JAR)&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;java -jar bigquery-antipattern-recognition.jar \\
  --query &quot;SELECT * FROM \\`project.dataset.table1\\`&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;INFORMATION_SCHEMA &amp;rarr; BigQuery 테이블 저장&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;docker run -v ~/.config:/root/.config -i bigquery-antipattern-recognition \\
  --read_from_info_schema \\
  --info_schema_project my-project \\
  --info_schema_region us \\
  --read_from_info_schema_days 7 \\
  --info_schema_top_n_percentage_of_jobs 0.1 \\
  --processing_project_id my-project \\
  --output_table &quot;my-project.dataset.antipattern_output&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 기반 SQL 자동 재작성&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;docker run -v ~/.config:/root/.config -i bigquery-antipattern-recognition \\
  --query &quot;SELECT col1 FROM table1 WHERE col2 LIKE '%abc%' AND col3 = 1&quot; \\
  --rewrite_sql \\
  --processing_project_id my-project
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로컬 파일 &amp;rarr; CSV 출력&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;docker run -v /local/path:/data -i bigquery-antipattern-recognition \\
  --input_file_path /data/query.sql \\
  --output_file_path /data/output.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;폴더 내 전체 SQL 파일 분석&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;docker run -v /local/path:/data -i bigquery-antipattern-recognition \\
  --input_folder_path /data/sql_files \\
  --output_file_path /data/output.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출력 테이블 DDL&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE OR REPLACE TABLE `project.dataset.antipattern_output_table` (
  job_id            STRING,
  user_email        STRING,
  query             STRING,
  recommendation    ARRAY&amp;lt;STRUCT&amp;lt;name STRING, description STRING&amp;gt;&amp;gt;,
  slot_hours        FLOAT64,
  optimized_sql     STRING,
  process_timestamp TIMESTAMP
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과 조회 쿼리&lt;/h2&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT job_id, user_email, query, recommendation, slot_hours
FROM `project.dataset.antipattern_output_table`
ORDER BY slot_hours DESC
LIMIT 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;sqlcheck&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범용 SQL 안티패턴 탐지 CLI 도구 (C++)입니다. 29개 패턴을 4개 카테고리로 분류하여 분석합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;쿼리 안티패턴 16개 (실무 관련도 높음)&lt;/h2&gt;
&lt;p&gt;코드 이름 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3001&lt;/td&gt;
&lt;td&gt;SELECT *&lt;/td&gt;
&lt;td&gt;전체 컬럼 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3002&lt;/td&gt;
&lt;td&gt;NULL Usage&lt;/td&gt;
&lt;td&gt;NULL 오용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3004&lt;/td&gt;
&lt;td&gt;String Concatenation&lt;/td&gt;
&lt;td&gt;문자열 연결 성능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3005&lt;/td&gt;
&lt;td&gt;GROUP BY Usage&lt;/td&gt;
&lt;td&gt;GROUP BY 오용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3006&lt;/td&gt;
&lt;td&gt;ORDER BY RAND&lt;/td&gt;
&lt;td&gt;RAND() 정렬 &amp;mdash; 전체 스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3007&lt;/td&gt;
&lt;td&gt;Pattern Matching&lt;/td&gt;
&lt;td&gt;앞자리 와일드카드 LIKE '%...'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3008&lt;/td&gt;
&lt;td&gt;Spaghetti Query&lt;/td&gt;
&lt;td&gt;지나치게 복잡한 단일 쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3009&lt;/td&gt;
&lt;td&gt;Reduce JOINs&lt;/td&gt;
&lt;td&gt;과도한 JOIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3010&lt;/td&gt;
&lt;td&gt;Unnecessary DISTINCT&lt;/td&gt;
&lt;td&gt;불필요한 DISTINCT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3012&lt;/td&gt;
&lt;td&gt;HAVING Clause&lt;/td&gt;
&lt;td&gt;HAVING 절 오용 (WHERE로 대체 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3013&lt;/td&gt;
&lt;td&gt;Nested Subqueries&lt;/td&gt;
&lt;td&gt;중첩 서브쿼리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3014&lt;/td&gt;
&lt;td&gt;OR Usage&lt;/td&gt;
&lt;td&gt;OR 연산자 &amp;mdash; 인덱스 미활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3015&lt;/td&gt;
&lt;td&gt;UNION Usage&lt;/td&gt;
&lt;td&gt;UNION (UNION ALL 권고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3016&lt;/td&gt;
&lt;td&gt;DISTINCT &amp;amp; JOIN&lt;/td&gt;
&lt;td&gt;DISTINCT + JOIN 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;# macOS (DMG)
wget &amp;lt;https://github.com/jarulraj/sqlcheck/releases/download/v1.3/sqlcheck-x86_64.dmg&amp;gt;
cp /Volumes/sqlcheck-x86_64/bin/sqlcheck /usr/local/bin/

# Ubuntu/Debian (.deb)
wget &amp;lt;https://github.com/jarulraj/sqlcheck/releases/download/v1.3/sqlcheck-x86_64.deb&amp;gt;
dpkg -i sqlcheck-x86_64.deb

# CentOS/Fedora (.rpm)
wget &amp;lt;https://github.com/jarulraj/sqlcheck/releases/download/v1.3/sqlcheck-x86_64.rpm&amp;gt;
yum --nogpgcheck localinstall sqlcheck-x86_64.rpm

# 소스 빌드 (g++ 4.9+, CMake 필요)
git clone --recursive &amp;lt;https://github.com/jarulraj/sqlcheck.git&amp;gt;
./bootstrap
cd build &amp;amp;&amp;amp; cmake -DCMAKE_BUILD_TYPE=RELEASE .. &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CLI 플래그&lt;/h2&gt;
&lt;p&gt;플래그 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-f, --file_name&lt;/td&gt;
&lt;td&gt;분석할 SQL 파일 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-r, --risk_level&lt;/td&gt;
&lt;td&gt;탐지 위험도 필터 (1: 전체, 2: MEDIUM 이상, 3: HIGH만)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-c, --color_mode&lt;/td&gt;
&lt;td&gt;컬러 출력 활성화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-v, --verbose_mode&lt;/td&gt;
&lt;td&gt;상세 정보 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--stdin&lt;/td&gt;
&lt;td&gt;표준 입력에서 SQL 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQL 파일 분석&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sqlcheck -f my_query.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-------------------------------------------------
SQL ANTI-PATTERN REPORT :: my_query.sql
-------------------------------------------------

[my_query.sql]: (HIGH RISK) (QUERY ANTI-PATTERN) SELECT *
Never use * in a SELECT. Always list out the columns needed.
Pattern: SELECT *

[my_query.sql]: (LOW RISK) (QUERY ANTI-PATTERN) SPAGHETTI QUERY ALERT
This query is too complex. Consider splitting into simpler queries.

======================
Total Anti-Patterns: 2
High Risk: 1  Medium Risk: 0  Low Risk: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상세 출력 + 색상 모드&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sqlcheck -f my_query.sql -v -c
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HIGH 위험도만 탐지&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sqlcheck -f my_query.sql -r 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이프 입력 (stdin)&lt;/h3&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;echo &quot;SELECT * FROM orders JOIN customers ON 1=1&quot; | sqlcheck --stdin
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여러 파일 일괄 분석&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;for f in ./queries/*.sql; do
  echo &quot;=== $f ===&quot;
  sqlcheck -f &quot;$f&quot; -r 2
done
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CI/CD 파이프라인 연동&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# 안티패턴 발견 시 비정상 종료 &amp;rarr; CI 실패 처리 가능
sqlcheck -f migration.sql -r 3 || exit 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;위험도 레벨&lt;/h2&gt;
&lt;p&gt;레벨 범위 언제 사용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;LOW 이상 전체&lt;/td&gt;
&lt;td&gt;코드 리뷰 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;MEDIUM 이상&lt;/td&gt;
&lt;td&gt;일반 개발 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;HIGH만&lt;/td&gt;
&lt;td&gt;CI/CD 게이트, 배포 전 필수 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;AltimateAI / altimate-code&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기반 데이터 엔지니어링 도구입니다. 10개 웨어하우스를 지원하며, Claude Code 통합이 가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;탐지 안티패턴 (19개 규칙, 신뢰도 점수 포함)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SELECT *&lt;/li&gt;
&lt;li&gt;카르테시안 조인 (조건 없는 JOIN)&lt;/li&gt;
&lt;li&gt;Non-sargable 조건절 (함수가 필터 컬럼에 적용)&lt;/li&gt;
&lt;li&gt;상관 서브쿼리 (Correlated Subquery)&lt;/li&gt;
&lt;li&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;15개 추가 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지원 플랫폼&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snowflake, BigQuery, Databricks, PostgreSQL, Redshift, ClickHouse, DuckDB, MySQL, SQL Server, Oracle, SQLite, MongoDB&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 기능&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FinOps 분석&lt;/b&gt;: 비용 소비 쿼리 탐지, 웨어하우스 크기 최적화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;열 수준 계보&lt;/b&gt;: 조인/CTE/서브쿼리를 통해 컬럼 추적&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dbt 통합&lt;/b&gt;: 매니페스트 파싱, 테스트 생성, 영향 분석&lt;/li&gt;
&lt;li&gt;&lt;b&gt;교차 방언 변환&lt;/b&gt;: Snowflake &amp;harr; BigQuery &amp;harr; Databricks SQL 자동 변환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PII 감지&lt;/b&gt;: 30+ 패턴으로 민감 정보 스캔&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;npm install -g altimate-code

# LLM API 키 설정 (하나 이상 필요)
export ANTHROPIC_API_KEY=your_key
export OPENAI_API_KEY=your_key
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초기 설정&lt;/h2&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;altimate
/connect        # LLM 공급자 선택 및 API 키 입력
/discover       # dbt 프로젝트, 웨어하우스 연결 자동 감지
/configure-claude  # Claude Code와 연동
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터베이스 연결 방식&lt;/h2&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# ~/.dbt/profiles.yml 자동 감지 또는 직접 설정
my_snowflake:
  type: snowflake
  account: myorg.us-east-1
  user: myuser
  password: &quot;{{ env_var('SNOWFLAKE_PASSWORD') }}&quot;
  database: MY_DB
  schema: PUBLIC
  warehouse: MY_WH
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에이전트 모드&lt;/h2&gt;
&lt;p&gt;모드 역할 권한 권장 환경&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Analyst&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터 탐색, SELECT 쿼리&lt;/td&gt;
&lt;td&gt;읽기 전용&lt;/td&gt;
&lt;td&gt;프로덕션 (안전)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Builder&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dbt 모델/SQL 파이프라인 생성&lt;/td&gt;
&lt;td&gt;읽기/쓰기 (DROP 금지)&lt;/td&gt;
&lt;td&gt;개발 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Plan&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;접근 방식 수립만&lt;/td&gt;
&lt;td&gt;파일 읽기만&lt;/td&gt;
&lt;td&gt;설계 단계&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 CLI 명령어 사용 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;안티패턴 탐지&lt;/h3&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;&amp;gt; Analyze this query for issues:
  SELECT * FROM orders o
  JOIN customers c ON o.id = c.order_id
  WHERE YEAR(o.created_at) = 2024
  ORDER BY o.total_amount
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;[HIGH] SELECT * detected &amp;mdash; specify required columns (confidence: 98%)
[HIGH] Non-sargable predicate: YEAR(created_at)
  &amp;rarr; Fix: created_at &amp;gt;= '2024-01-01' AND created_at &amp;lt; '2025-01-01'
[MAJOR] ORDER BY without LIMIT &amp;mdash; full sort on potentially large dataset
  &amp;rarr; Fix: add LIMIT clause or use window function
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SQL 방언 변환&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;&amp;gt; /sql-translate this Snowflake query to Databricks:
  SELECT DATEADD(day, 7, CURRENT_DATE()), ZEROIFNULL(revenue) FROM sales
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dbt 테스트 자동 생성&lt;/h3&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;&amp;gt; /generate-tests for models/staging/stg_orders.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비용 리포트 조회&lt;/h3&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;&amp;gt; /cost-report
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;컬럼 계보 추적&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;&amp;gt; Trace column lineage for customer_id in fact_orders
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PII 탐지&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;&amp;gt; Scan schema my_schema for PII columns
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지원 LLM&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic Claude &amp;middot; OpenAI &amp;middot; Google Gemini &amp;middot; Amazon Bedrock &amp;middot; Azure OpenAI &amp;middot; Mistral &amp;middot; Groq &amp;middot; Ollama&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;sqlglot (자체 구현 시 파서 기반)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python SQL 파서 및 트랜스파일러입니다. Trino&amp;middot;Databricks&amp;middot;Snowflake 등 31개 방언을 공식 지원하며, 자체 안티패턴 엔진 구현 시 기반으로 활용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install sqlglot

# C 확장 버전 (성능 향상)
pip install &quot;sqlglot[c]&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;지원 방언 (주요)&lt;/h2&gt;
&lt;p&gt;방언 지정 키워드&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;&quot;trino&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;&quot;databricks&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;&quot;snowflake&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;&quot;bigquery&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spark SQL&lt;/td&gt;
&lt;td&gt;&quot;spark&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presto&lt;/td&gt;
&lt;td&gt;&quot;presto&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB&lt;/td&gt;
&lt;td&gt;&quot;duckdb&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;&quot;postgres&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 파싱&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;import sqlglot
from sqlglot import exp, parse_one

# 방언을 명시해야 정확한 파싱이 가능합니다
ast = parse_one(
    &quot;SELECT * FROM orders WHERE YEAR(created_at) = 2024 ORDER BY id&quot;,
    dialect=&quot;trino&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AST 순회 방법&lt;/h2&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;# find: 첫 번째 매칭 노드 반환
order = ast.find(exp.Order)

# find_all: 모든 매칭 노드 반환
for col in ast.find_all(exp.Column):
    print(col.alias_or_name)

# walk: 전체 노드 순회
for node in ast.walk():
    if isinstance(node, exp.Star):
        print(&quot;SELECT * 발견&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;안티패턴 탐지 예시 코드&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from sqlglot import exp, parse_one
from dataclasses import dataclass
from typing import List

@dataclass
class Issue:
    severity: str   # CRITICAL / MAJOR / MINOR
    message: str
    suggestion: str

def detect_antipatterns(query: str, dialect: str = &quot;trino&quot;) -&amp;gt; List[Issue]:
    ast = parse_one(query, dialect=dialect)
    issues = []

    # [CRITICAL] SELECT *
    for node in ast.find_all(exp.Star):
        issues.append(Issue(
            severity=&quot;CRITICAL&quot;,
            message=&quot;SELECT * 사용 &amp;mdash; 불필요한 전체 컬럼 조회&quot;,
            suggestion=&quot;필요한 컬럼만 명시하세요&quot;
        ))

    # [CRITICAL] Cartesian JOIN (조건 없는 JOIN)
    for join in ast.find_all(exp.Join):
        if not join.args.get(&quot;on&quot;) and not join.args.get(&quot;using&quot;):
            if str(join.args.get(&quot;kind&quot;, &quot;&quot;)).upper() not in (&quot;CROSS&quot;,):
                issues.append(Issue(
                    severity=&quot;CRITICAL&quot;,
                    message=&quot;조건 없는 JOIN 감지 &amp;mdash; Cartesian product 발생 가능&quot;,
                    suggestion=&quot;ON 또는 USING 조건을 추가하세요&quot;
                ))

    # [CRITICAL] Non-sargable predicate (WHERE 절 함수 적용)
    where = ast.find(exp.Where)
    if where:
        for func in where.find_all(exp.Func):
            if any(isinstance(c, exp.Column) for c in func.find_all(exp.Column)):
                issues.append(Issue(
                    severity=&quot;CRITICAL&quot;,
                    message=f&quot;WHERE 절 함수 적용: {func.sql()} &amp;mdash; 파티션 프루닝 불가&quot;,
                    suggestion=&quot;함수를 우변으로 이동하거나 범위 조건으로 변환하세요&quot;
                ))

    # [MAJOR] ORDER BY without LIMIT
    if ast.find(exp.Order) and not ast.find(exp.Limit):
        issues.append(Issue(
            severity=&quot;MAJOR&quot;,
            message=&quot;ORDER BY에 LIMIT 없음 &amp;mdash; 전체 데이터 정렬 발생&quot;,
            suggestion=&quot;LIMIT 절을 추가하세요&quot;
        ))

    # [MAJOR] UNION (UNION ALL 권고)
    for union in ast.find_all(exp.Union):
        if not isinstance(union, exp.UnionAll):
            issues.append(Issue(
                severity=&quot;MAJOR&quot;,
                message=&quot;UNION 사용 &amp;mdash; 중복 제거 정렬 비용 발생&quot;,
                suggestion=&quot;중복 허용 시 UNION ALL로 변경하세요&quot;
            ))

    return issues

# 실행 예시
query = &quot;&quot;&quot;
    SELECT *
    FROM orders o
    JOIN customers c ON 1=1
    WHERE YEAR(o.created_at) = 2024
    ORDER BY o.id
&quot;&quot;&quot;

for issue in detect_antipatterns(query, dialect=&quot;trino&quot;):
    print(f&quot;[{issue.severity}] {issue.message}&quot;)
    print(f&quot;  &amp;rarr; {issue.suggestion}&quot;)
    print()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;[CRITICAL] SELECT * 사용 &amp;mdash; 불필요한 전체 컬럼 조회
  &amp;rarr; 필요한 컬럼만 명시하세요

[CRITICAL] 조건 없는 JOIN 감지 &amp;mdash; Cartesian product 발생 가능
  &amp;rarr; ON 또는 USING 조건을 추가하세요

[CRITICAL] WHERE 절 함수 적용: YEAR(o.created_at) &amp;mdash; 파티션 프루닝 불가
  &amp;rarr; 함수를 우변으로 이동하거나 범위 조건으로 변환하세요

[MAJOR] ORDER BY에 LIMIT 없음 &amp;mdash; 전체 데이터 정렬 발생
  &amp;rarr; LIMIT 절을 추가하세요
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방언 간 SQL 변환&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import sqlglot

# Snowflake &amp;rarr; Trino 변환
result = sqlglot.transpile(
    &quot;SELECT DATEADD(day, 7, CURRENT_DATE()), ZEROIFNULL(revenue) FROM sales&quot;,
    read=&quot;snowflake&quot;,
    write=&quot;trino&quot;
)[0]
print(result)
# &amp;rarr; SELECT DATE_ADD('day', 7, CURRENT_DATE), COALESCE(revenue, 0) FROM sales

# Databricks &amp;rarr; Snowflake 변환
result = sqlglot.transpile(
    &quot;SELECT DATE_FORMAT(order_date, 'yyyy-MM-dd') FROM orders&quot;,
    read=&quot;databricks&quot;,
    write=&quot;snowflake&quot;
)[0]
print(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구문 오류 탐지&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import sqlglot

try:
    sqlglot.transpile(&quot;SELECT foo FROM (SELECT baz FROM t&quot;)
except sqlglot.errors.ParseError as e:
    for err in e.errors:
        print(f&quot;Line {err['line']}, Col {err['col']}: {err['description']}&quot;)
        print(f&quot;Context: ...{err['start_context']}{err['highlight']}...&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;Line 1, Col 34: Expecting )
Context: ...SELECT foo FROM (SELECT baz FROM t...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Scope API (서브쿼리&amp;middot;CTE 정확한 분석)&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from sqlglot import parse_one, exp
from sqlglot.optimizer.scope import build_scope

query = &quot;&quot;&quot;
WITH cte AS (SELECT id FROM orders)
SELECT * FROM cte JOIN cte AS cte2 ON cte.id = cte2.id
&quot;&quot;&quot;

ast = parse_one(query, dialect=&quot;trino&quot;)
root = build_scope(ast)

# CTE 중복 참조 탐지
for scope in root.traverse():
    cte_refs = {}
    for name, (node, source) in scope.selected_sources.items():
        if isinstance(source, exp.Subquery):
            cte_name = source.alias
            cte_refs[cte_name] = cte_refs.get(cte_name, 0) + 1

    for cte_name, count in cte_refs.items():
        if count &amp;gt;= 2:
            print(f&quot;[MAJOR] CTE '{cte_name}' 중복 참조 ({count}회) &amp;mdash; 반복 연산 발생&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;통합 안티패턴 카탈로그&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Critical &amp;mdash; 반드시 수정 (실행 차단 권고)&lt;/h2&gt;
&lt;p&gt;안티패턴 설명 영향&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SELECT *&lt;/td&gt;
&lt;td&gt;전체 컬럼 조회&lt;/td&gt;
&lt;td&gt;스캔 비용 폭증, 컬럼 추가 시 부작용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cartesian JOIN&lt;/td&gt;
&lt;td&gt;조건 없는 JOIN (CROSS JOIN)&lt;/td&gt;
&lt;td&gt;행 수 곱 증가 &amp;rarr; OOM&amp;middot;무한 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-sargable predicate&lt;/td&gt;
&lt;td&gt;필터 컬럼에 함수 적용: WHERE YEAR(date) = 2024&lt;/td&gt;
&lt;td&gt;파티션 프루닝&amp;middot;인덱스 무력화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correlated subquery in WHERE&lt;/td&gt;
&lt;td&gt;행마다 서브쿼리 재실행&lt;/td&gt;
&lt;td&gt;O(N&amp;sup2;) 복잡도&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Major &amp;mdash; 강하게 권고&lt;/h2&gt;
&lt;p&gt;안티패턴 설명 영향&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ORDER BY without LIMIT&lt;/td&gt;
&lt;td&gt;전체 정렬 후 페이지네이션 없음&lt;/td&gt;
&lt;td&gt;전체 데이터 메모리 적재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTE 중복 참조&lt;/td&gt;
&lt;td&gt;동일 CTE를 여러 번 참조&lt;/td&gt;
&lt;td&gt;반복 연산 (DB에 따라 materialized 미보장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IN 서브쿼리 without DISTINCT&lt;/td&gt;
&lt;td&gt;IN (SELECT id FROM ...)&lt;/td&gt;
&lt;td&gt;중복 비교로 성능 저하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISTINCT + JOIN 조합&lt;/td&gt;
&lt;td&gt;JOIN 후 DISTINCT로 중복 제거&lt;/td&gt;
&lt;td&gt;불필요한 해시 빌드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중첩 서브쿼리&lt;/td&gt;
&lt;td&gt;SELECT&amp;middot;FROM 절 내 서브쿼리 중첩&lt;/td&gt;
&lt;td&gt;최적화 불가 구간 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LIKE '%keyword'&lt;/td&gt;
&lt;td&gt;앞자리 와일드카드&lt;/td&gt;
&lt;td&gt;전체 스캔 강제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UNION (UNION ALL 대신)&lt;/td&gt;
&lt;td&gt;중복 제거 정렬 포함&lt;/td&gt;
&lt;td&gt;추가 정렬 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Minor &amp;mdash; 권고&lt;/h2&gt;
&lt;p&gt;안티패턴 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HAVING (WHERE로 대체 가능)&lt;/td&gt;
&lt;td&gt;집계 전 필터는 WHERE로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORDER BY RAND()&lt;/td&gt;
&lt;td&gt;전체 스캔 후 랜덤 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;불필요한 DISTINCT&lt;/td&gt;
&lt;td&gt;중복이 없는 상황에서 DISTINCT 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;지나치게 복잡한 단일 쿼리&lt;/td&gt;
&lt;td&gt;CTE로 분리 권고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OR 연산자 남용&lt;/td&gt;
&lt;td&gt;UNION ALL 또는 IN으로 대체 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REGEXP_CONTAINS (BigQuery)&lt;/td&gt;
&lt;td&gt;단순 패턴은 LIKE로 충분&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;플랫폼별 특화 안티패턴&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Trino&lt;/h2&gt;
&lt;p&gt;안티패턴 설명 탐지 시점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;소파일 과다 (Too many splits)&lt;/td&gt;
&lt;td&gt;파티션당 소파일 &amp;rarr; 과도한 split 생성&lt;/td&gt;
&lt;td&gt;EXPLAIN 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic filtering 미활용&lt;/td&gt;
&lt;td&gt;브로드캐스트 조인 없이 대형 테이블 풀스캔&lt;/td&gt;
&lt;td&gt;EXPLAIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predicate pushdown 비활성&lt;/td&gt;
&lt;td&gt;커넥터에 필터가 내려가지 않음&lt;/td&gt;
&lt;td&gt;EXPLAIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JOIN 순서 비최적&lt;/td&gt;
&lt;td&gt;큰 테이블이 build side가 됨&lt;/td&gt;
&lt;td&gt;EXPLAIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;컬럼형 포맷 미사용&lt;/td&gt;
&lt;td&gt;ORC/Parquet 대신 CSV/JSON&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Databricks / Spark&lt;/h2&gt;
&lt;p&gt;안티패턴 설명 탐지 시점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UDF 남용&lt;/td&gt;
&lt;td&gt;Python UDF &amp;rarr; Spark native 함수로 대체&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;collect() 호출&lt;/td&gt;
&lt;td&gt;드라이버에 전체 데이터 수집 &amp;rarr; OOM&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;explode() 후 집계 없는 JOIN&lt;/td&gt;
&lt;td&gt;행 폭발 후 바로 조인&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shuffle partition 미조정&lt;/td&gt;
&lt;td&gt;기본 200 파티션 그대로 사용&lt;/td&gt;
&lt;td&gt;EXPLAIN EXTENDED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;autoMerge 스키마 진화&lt;/td&gt;
&lt;td&gt;예상 못한 스키마 변경&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liquid Clustering 미사용&lt;/td&gt;
&lt;td&gt;Z-order 대신 Liquid Clustering 미전환&lt;/td&gt;
&lt;td&gt;메타데이터 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small file 누적&lt;/td&gt;
&lt;td&gt;OPTIMIZE/VACUUM 미실행&lt;/td&gt;
&lt;td&gt;메타데이터 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Snowflake&lt;/h2&gt;
&lt;p&gt;안티패턴 설명 탐지 시점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;필터 컬럼에 함수 적용&lt;/td&gt;
&lt;td&gt;WHERE DATE_TRUNC(...) &amp;rarr; 파티션 프루닝 불가&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clustering key 미사용&lt;/td&gt;
&lt;td&gt;대형 테이블 파티션 pruning 없음&lt;/td&gt;
&lt;td&gt;EXPLAIN / 메타데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VARIANT/JSON 전체 스캔&lt;/td&gt;
&lt;td&gt;반구조 데이터 전체 파싱&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small file INSERT&lt;/td&gt;
&lt;td&gt;COPY INTO 대신 단건 INSERT 반복&lt;/td&gt;
&lt;td&gt;정적 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warehouse 과대 설정&lt;/td&gt;
&lt;td&gt;쿼리 복잡도 대비 X-Large 사용&lt;/td&gt;
&lt;td&gt;실행 후 통계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RESULT_SCAN 미활용&lt;/td&gt;
&lt;td&gt;동일 쿼리 반복 실행 (캐시 미활용)&lt;/td&gt;
&lt;td&gt;히스토리 분석&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;구현 방향&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 흐름&lt;/h2&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;쿼리 입력
   &amp;darr;
[1] 정적 분석 (AST)         &amp;rarr; Critical/Major 즉시 차단&amp;middot;경고
   &amp;darr;
[2] EXPLAIN 분석            &amp;rarr; 실행 계획 기반 추가 경고
   &amp;darr;
[3] 실행 (선택)
   &amp;darr;
[4] 실행 후 프로파일 분석    &amp;rarr; 실제 비용 기반 튜닝 포인트
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pre-execution: 정적 분석 (Dry-run)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 실행 없이 SQL 텍스트 &amp;rarr; AST 파싱 &amp;rarr; 패턴 매칭 방식으로 동작합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;추천 도구&lt;/b&gt;: sqlglot (Python) &amp;mdash; Trino&amp;middot;Databricks&amp;middot;Snowflake 방언 모두 지원, AST 변환 가능&lt;/li&gt;
&lt;li&gt;탐지 가능: SELECT *, Cartesian JOIN, Non-sargable predicate, UNION vs UNION ALL, 중첩 서브쿼리, ORDER BY without LIMIT 등&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pre-execution: EXPLAIN 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에 EXPLAIN을 실행하여 (데이터 스캔 없음) 실행 계획을 파싱합니다.&lt;/p&gt;
&lt;p&gt;플랫폼 명령어 얻을 수 있는 정보&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;EXPLAIN query&lt;/td&gt;
&lt;td&gt;논리/분산 실행 계획, 예상 행 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;EXPLAIN EXTENDED query&lt;/td&gt;
&lt;td&gt;논리/물리 플랜, AQE 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;EXPLAIN USING TABULAR query&lt;/td&gt;
&lt;td&gt;파티션 프루닝 여부, 예상 스캔 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;dry_run=True (API)&lt;/td&gt;
&lt;td&gt;예상 바이트 스캔 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EXPLAIN으로 탐지 가능한 항목: Full scan (파티션 프루닝 없음), 비효율적 JOIN 순서, 브로드캐스트 JOIN 미활용, 동적 필터 미적용입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Post-execution: 프로파일 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 완료 후 통계 데이터를 수집하여 실제 병목을 진단합니다.&lt;/p&gt;
&lt;p&gt;플랫폼 데이터 소스 분석 포인트&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trino&lt;/td&gt;
&lt;td&gt;system.runtime.queries&lt;/td&gt;
&lt;td&gt;실제 스캔 바이트, CPU 시간, 스필 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Spark UI / DESCRIBE HISTORY&lt;/td&gt;
&lt;td&gt;스테이지별 실행 시간, 셔플 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Query Profile / QUERY_HISTORY&lt;/td&gt;
&lt;td&gt;파티션 스캔율, Spill to disk, 원격 스필&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;INFORMATION_&lt;a href=&quot;http://SCHEMA.JOBS&quot;&gt;SCHEMA.JOBS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;실제 바이트 처리, Slot ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;우선순위 체계&lt;/h2&gt;
&lt;p&gt;심각도 액션 예시&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;  Critical&lt;/td&gt;
&lt;td&gt;실행 차단 또는 명시적 확인 요구&lt;/td&gt;
&lt;td&gt;Cartesian JOIN, Non-sargable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  Major&lt;/td&gt;
&lt;td&gt;경고 + 수정 제안 코드 제공&lt;/td&gt;
&lt;td&gt;ORDER BY without LIMIT, CTE 중복 참조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  Minor&lt;/td&gt;
&lt;td&gt;선택적 최적화 제안&lt;/td&gt;
&lt;td&gt;UNION &amp;rarr; UNION ALL, OR &amp;rarr; IN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추천 구현 스택&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SQL 파싱&lt;/b&gt;: sqlglot (Python) &amp;mdash; 다방언 지원, AST 변환, 방언 간 변환 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안티패턴 규칙 엔진&lt;/b&gt;: 규칙 기반 (Rule-based) + LLM 보완&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EXPLAIN 파싱&lt;/b&gt;: 플랫폼별 EXPLAIN 출력 파서 (JSON 형태 권장)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레퍼런스 아키텍처&lt;/b&gt;: BigQuery Anti-Pattern Recognition (오픈소스) 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;도구 선택 가이드&lt;/h1&gt;
&lt;p&gt;상황 추천 도구&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery 환경에서 전체 쿼리 자동 감사&lt;/td&gt;
&lt;td&gt;BigQuery Anti-Pattern Recognition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB 무관한 SQL 파일 빠른 CLI 검사&lt;/td&gt;
&lt;td&gt;sqlcheck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&amp;middot;Databricks 멀티 웨어하우스 + AI 제안&lt;/td&gt;
&lt;td&gt;AltimateAI/altimate-code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자체 안티패턴 엔진 개발, 다방언 지원 필요&lt;/td&gt;
&lt;td&gt;sqlglot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD 게이트에서 HIGH 패턴만 차단&lt;/td&gt;
&lt;td&gt;sqlcheck (-r 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;방언 간 SQL 마이그레이션&lt;/td&gt;
&lt;td&gt;sqlglot (transpile)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;참고 링크&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/blog/products/data-analytics/bigquery-anti-pattern-recognition-tool-optimizes-performance&quot;&gt;BigQuery Anti-Pattern Recognition Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleCloudPlatform/bigquery-antipattern-recognition&quot;&gt;GoogleCloudPlatform/bigquery-antipattern-recognition (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jarulraj/sqlcheck&quot;&gt;jarulraj/sqlcheck (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/AltimateAI/altimate-code&quot;&gt;AltimateAI/altimate-code (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tobymao/sqlglot&quot;&gt;tobymao/sqlglot (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tobymao/sqlglot/blob/main/posts/ast_primer.md&quot;&gt;sqlglot AST Primer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DB</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/750</guid>
      <comments>https://brownbears.tistory.com/750#entry750comment</comments>
      <pubDate>Tue, 28 Apr 2026 01:48:38 +0900</pubDate>
    </item>
    <item>
      <title>레이크 하우스란</title>
      <link>https://brownbears.tistory.com/707</link>
      <description>&lt;h1&gt;레이크하우스 오픈 테이블 포맷 완전 비교&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delta Lake &amp;middot; Apache Iceberg &amp;middot; Apache Hudi &amp;middot; Apache Paimon &amp;middot; DuckLake(2026) 포맷의 메타데이터 구조, 카탈로그 아키텍처, 심화 기술 특징을 비교합니다. Apache XTable(상호운용 레이어)도 별도 정리합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;레이크하우스란?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;레이크하우스(Lakehouse)&lt;/b&gt; = 데이터 레이크의 유연성&amp;middot;비용 효율성 + 데이터 웨어하우스의 ACID 트랜잭션&amp;middot;스키마 관리&amp;middot;거버넌스를 &lt;b&gt;오픈 파일 포맷&lt;/b&gt; 위에서 직접 제공하는 통합 아키텍처&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전통 2계층 아키텍처의 문제&lt;/h3&gt;
&lt;p&gt;구분 데이터 레이크 데이터 웨어하우스&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정형&amp;middot;비정형 대량 저장, 비용 효율적&lt;/td&gt;
&lt;td&gt;ACID 트랜잭션, 스키마 강제, 우수한 쿼리 성능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;품질&amp;middot;일관성&amp;middot;거버넌스 관리 어려움&lt;/td&gt;
&lt;td&gt;ETL 필수, 높은 비용, 비정형 데이터 미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 계층 혼용 시 &lt;b&gt;데이터 중복 &amp;middot; ETL 복잡성 &amp;middot; 시스템 간 불일치 &amp;middot; 높은 TCO&lt;/b&gt; 문제가 발생합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오픈 테이블 포맷이 해결하는 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 테이블 포맷(Delta Lake, Iceberg, Hudi, Paimon 등)은 &lt;b&gt;객체 스토리지(S3/OSS) 위에서&lt;/b&gt; 데이터 웨어하우스 수준의 기능을 직접 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ACID 트랜잭션&lt;/b&gt;: 병렬 읽기/쓰기 충돌 방지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스키마 관리&lt;/b&gt;: 강제(enforcement) + 진화(evolution)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 버전 관리&lt;/b&gt;: Time Travel로 과거 스냅샷 조회&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 워크로드 지원&lt;/b&gt;: BI/SQL &amp;middot; ML/AI &amp;middot; 실시간 스트리밍을 단일 플랫폼에서&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포맷 분류 체계&lt;/h2&gt;
&lt;p&gt;분류 포맷 핵심 패러다임&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전통 파일 기반&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;Flat JSON 로그 + Parquet 체크포인트, 카탈로그 독립&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전통 파일 기반&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;계층적 스냅샷 트리, 외부 카탈로그 필수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전통 파일 기반&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Hudi&lt;/td&gt;
&lt;td&gt;Timeline 기반 CDC/Upsert 특화, COW/MOR 이중 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전통 파일 기반&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Paimon&lt;/td&gt;
&lt;td&gt;LSM Tree + 스트리밍 네이티브, Flink 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SQL DB 기반&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DuckLake&lt;/td&gt;
&lt;td&gt;SQL DB가 카탈로그 + 메타데이터 역할 (2025~2026 신흥)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상호운용 레이어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache XTable&lt;/td&gt;
&lt;td&gt;포맷 간 메타데이터 변환 (독립 포맷 아님)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실시간 스트리밍 스토리지&lt;/b&gt; (Lakehouse 포맷 아님)&lt;/td&gt;
&lt;td&gt;Apache Fluss&lt;/td&gt;
&lt;td&gt;분산 스트리밍 스토리지 시스템. Kafka 후계자. Paimon/Iceberg의 hot tier 보완재 (ASF Incubating, 2025~2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 비교 요약&lt;/h2&gt;
&lt;p&gt;항목 Delta Lake Apache Iceberg Apache Hudi Apache Paimon DuckLake&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;기원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;Netflix&lt;/td&gt;
&lt;td&gt;Uber&lt;/td&gt;
&lt;td&gt;Alibaba/Flink 커뮤니티&lt;/td&gt;
&lt;td&gt;DuckDB Labs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;출시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;2025 (v1.0: 2026.04)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메타데이터 저장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;_delta_log/ (JSON + Parquet)&lt;/td&gt;
&lt;td&gt;metadata.json &amp;rarr; Manifest 계층&lt;/td&gt;
&lt;td&gt;.hoodie/ Timeline&lt;/td&gt;
&lt;td&gt;Snapshot &amp;rarr; Manifest 계층&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SQL DB&lt;/b&gt; (SQLite / DuckDB / PostgreSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;외부 카탈로그 필수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;아니오 (경로 기반 독립)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예 (필수)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;아니오 (HMS 권장)&lt;/td&gt;
&lt;td&gt;아니오 (FilesystemCatalog 기본)&lt;/td&gt;
&lt;td&gt;아니오 (SQL DB 자체가 카탈로그)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;저장 구조&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Flat 로그 + Checkpoint&lt;/td&gt;
&lt;td&gt;계층적 스냅샷 트리&lt;/td&gt;
&lt;td&gt;Timeline + COW/MOR&lt;/td&gt;
&lt;td&gt;&lt;b&gt;LSM Tree&lt;/b&gt; &amp;bull; 스냅샷&lt;/td&gt;
&lt;td&gt;Parquet + SQL 메타데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;핵심 강점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spark 생태계, 배치 처리&lt;/td&gt;
&lt;td&gt;멀티 엔진 호환성&lt;/td&gt;
&lt;td&gt;CDC/Upsert 특화&lt;/td&gt;
&lt;td&gt;&lt;b&gt;스트리밍 네이티브&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;경량&amp;middot;단순&amp;middot;DuckDB 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;동시성 제어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;OCC + Coordinated Commits&lt;/td&gt;
&lt;td&gt;OCC (CAS)&lt;/td&gt;
&lt;td&gt;OCC + &lt;b&gt;NBCC&lt;/b&gt; (1.0)&lt;/td&gt;
&lt;td&gt;OCC (스냅샷 격리)&lt;/td&gt;
&lt;td&gt;SQL DB 트랜잭션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Time Travel&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;버전/타임스탬프&lt;/td&gt;
&lt;td&gt;스냅샷 ID/타임스탬프&lt;/td&gt;
&lt;td&gt;타임스탬프/커밋&lt;/td&gt;
&lt;td&gt;스냅샷 ID/타임스탬프&lt;/td&gt;
&lt;td&gt;SQL DB 쿼리 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;파티션 진화&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;X (Liquid Clustering 대체)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O (재작성 불필요)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X (단순 설계)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스트리밍 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;보통&lt;/td&gt;
&lt;td&gt;보통&lt;/td&gt;
&lt;td&gt;우수&lt;/td&gt;
&lt;td&gt;&lt;b&gt;최우수 (Flink 네이티브)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;미지원 (배치 중심)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Small File 자동화&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Databricks 전용&lt;/td&gt;
&lt;td&gt;수동 트리거&lt;/td&gt;
&lt;td&gt;비동기 서비스 분리&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Flink 내장 자동화&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Data Inlining으로 회피&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;벤더 중립성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간 (Databricks 주도)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;높음&lt;/b&gt; (30개+ 기업)&lt;/td&gt;
&lt;td&gt;높음 (ASF)&lt;/td&gt;
&lt;td&gt;중간 (Alibaba 주도)&lt;/td&gt;
&lt;td&gt;중간 (DuckDB Labs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전통 파일 기반 포맷&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Delta Lake&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메타데이터 구조: _delta_log/&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delta Lake는 &lt;b&gt;카탈로그 없이 독립 동작&lt;/b&gt;이 가능한 설계입니다. 모든 트랜잭션 정보가 _delta_log/ 디렉토리에 완전히 내재화되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;&amp;lt;table-root&amp;gt;/
├── _delta_log/
│   ├── 00000000000000000000.json   # 최초 커밋
│   ├── 00000000000000000001.json
│   ├── 00000000000000000010.parquet  # 체크포인트 (10번째마다 자동)
│   ├── _last_checkpoint              # 최신 체크포인트 포인터
│   └── _sidecars/                    # V2 체크포인트 사이드카 (3.0+)
└── part-00000-xxxx.snappy.parquet
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 커밋 파일 액션 타입: protocol &amp;middot; metaData &amp;middot; add (파일 추가 + min/max 통계) &amp;middot; remove (논리 삭제) &amp;middot; commitInfo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 번호 자체가 순서를 보장하므로 외부 포인터가 불필요합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카탈로그 아키텍처&lt;/h4&gt;
&lt;p&gt;방식 설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;경로 기반 (카탈로그 없음)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;spark.read.format(&quot;delta&quot;).load(&quot;s3://...&quot;) &amp;mdash; 외부 서비스 불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hive Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가장 범용적. Spark, Trino, Presto 호환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Unity Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta 네이티브. 멀티클라우드 거버넌스, Row-level Security/Column Masking 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서버리스. Athena/EMR 연동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg REST 스펙 준수 오픈소스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ACID 트랜잭션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 OCC (Optimistic Concurrency Control). Delta 4.0부터 &lt;b&gt;Coordinated Commits&lt;/b&gt; 도입 &amp;mdash; DynamoDB 등 외부 Commit Coordinator가 커밋을 중재하여 멀티엔진 동시 쓰기를 안전하게 보장합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Deletion Vector (DV)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delta 3.1에서 &lt;b&gt;Compressed Bitmap 방식&lt;/b&gt; GA. 파일 재작성 없이 _delta_log/*.bin 사이드카 파일에 삭제 위치를 비트맵으로 저장합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 기능&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Time Travel&lt;/b&gt;: VERSION AS OF 42 / TIMESTAMP AS OF '2025-01-01'&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스키마 진화&lt;/b&gt;: mergeSchema (컬럼 추가), Column Mapping (컬럼명 변경/삭제, 파일 재작성 없음)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Liquid Clustering (3.0+)&lt;/b&gt;: 내부적으로 &lt;b&gt;Hilbert Curve&lt;/b&gt; 사용. Z-ORDER 대비 고차원 데이터 locality 우수. 증분 클러스터링으로 전체 재작성 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Delta Sharing&lt;/b&gt;: 크로스 플랫폼 데이터 공유 프로토콜 (데이터 복제 없이 서명된 URL 공유)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UniForm (3.0+)&lt;/b&gt;: 동일 Delta 파일 위에 Iceberg/Hudi 메타데이터 자동 생성 &amp;rarr; Snowflake, Athena 직접 읽기 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점 &amp;amp; 주의사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Databricks 전용 기능&lt;/b&gt;: Auto Optimize, Auto Loader, Bloom Filter Index, Primary Key 제약 &amp;mdash; OSS Delta에는 없음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파티션 진화 미지원&lt;/b&gt;: 파티션 변경 시 전체 데이터 재작성 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Small File 자동화&lt;/b&gt;: Databricks 환경 전용. OSS에서는 OPTIMIZE 수동 실행 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Apache Iceberg&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카탈로그가 필수인 이유&lt;/h4&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;오브젝트 스토리지(S3)는 원자적 파일 교체를 보장하지 않음
  &amp;rarr; v1.metadata.json vs v2.metadata.json &amp;mdash; 어느 것이 현재 유효한 버전인지 파일 목록만으로 알 수 없음
  &amp;rarr; 외부 서비스가 원자적 CAS(Compare-and-Swap)로 단일 포인터를 관리해야 함

카탈로그가 저장하는 것은 단 하나:
  prod_db.orders &amp;rarr; s3://.../metadata/v4.metadata.json
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메타데이터 4계층 구조&lt;/h4&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Catalog
  └── Namespace (Database)
        └── Table
              └── metadata.json  &amp;larr; 카탈로그가 가리키는 유일한 포인터
                    └── Snapshot
                          └── Manifest List (Avro, 파티션 범위 통계 포함)
                                └── Manifest File (Avro, 컬럼 min/max 통계 포함)
                                      └── Data Files (Parquet/ORC/Avro)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여러 버전 메타데이터 파일 관리&lt;/h4&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;metadata/
  v1.metadata.json   &amp;larr; 최초 생성
  v2.metadata.json   &amp;larr; INSERT 후 (v1은 삭제되지 않음)
  v3.metadata.json   &amp;larr; UPDATE 후
  v4.metadata.json   &amp;larr; 현재 버전 (카탈로그가 이 경로만 가리킴)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그는 여러 버전 중 &lt;b&gt;가장 최신 버전 파일 경로 하나&lt;/b&gt;만 포인터로 유지합니다. 이전 버전들은 Time Travel을 위해 보존됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3단계 프루닝 메커니즘&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;1단계: Manifest List &amp;rarr; 파티션 범위로 불필요한 Manifest File 스킵
2단계: Manifest File &amp;rarr; 컬럼 min/max 통계로 불필요한 Data File 스킵
3단계: Data File 내부 &amp;rarr; Parquet Row Group 통계로 Row Group 스킵
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ACID 트랜잭션: Snapshot Isolation + OCC&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 파일은 불변(immutable). Writer는 새 파일을 생성하고 카탈로그 포인터를 CAS로 원자 교체합니다. 충돌 시 rollback + retry.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Deletion Vector (Iceberg v3, 2025 GA)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v2의 Position/Equality Delete File 방식을 개선하여 v3에서 &lt;b&gt;Puffin 파일 기반 DV&lt;/b&gt; 도입.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Puffin 파일 (compact binary sidecar) 에 압축 비트맵으로 삭제 위치 저장&lt;/li&gt;
&lt;li&gt;데이터 파일 1개당 DV 1개만 허용, 신규 삭제 시 기존 DV와 merge&lt;/li&gt;
&lt;li&gt;AWS 벤치마크: v2 대비 delete 속도 &lt;b&gt;55% 향상&lt;/b&gt;, 저장 크기 &lt;b&gt;73.6% 감소&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 기능&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Hidden Partitioning&lt;/b&gt;: month(order_date), bucket(16, user_id) 등 변환 함수로 물리 파티션 자동 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파티션 진화&lt;/b&gt;: ALTER TABLE ... SET PARTITION SPEC (day(order_date)) &amp;mdash; 기존 데이터 재작성 없이 파티션 전략 변경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Time Travel&lt;/b&gt;: Snapshot ID 또는 타임스탬프 기반&lt;/li&gt;
&lt;li&gt;&lt;b&gt;REST Catalog 표준&lt;/b&gt;: OpenAPI 스펙 기반 Vendor-neutral 카탈로그 인터페이스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지원 카탈로그 구현체:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;구현체 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Snowflake 오픈소스 기증. RBAC + Credential Vending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Git 스타일 브랜치/태그. 다중 테이블 트랜잭션 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Lakekeeper&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Rust 기반 경량 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;관리형. AWS 생태계 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hive Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;레거시 호환성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;카탈로그 없이 동작 불가&lt;/b&gt; &amp;mdash; 운영 복잡도 증가&lt;/li&gt;
&lt;li&gt;Delete 파일(v2) 누적 시 읽기 성능 저하 &amp;rarr; 주기적 Compaction 필요&lt;/li&gt;
&lt;li&gt;Small File 자동화 도구 없음 (수동 rewrite_data_files 실행 의존)&lt;/li&gt;
&lt;li&gt;Clustering 알고리즘: Z-order만 지원, Hilbert Curve 미지원 (2026 기준)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Apache Hudi&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delta Lake와 유사한 설계 철학을 가지나, &lt;b&gt;CDC/Upsert에 특화&lt;/b&gt;된 포맷입니다. Uber가 설계한 목적 자체가 record-level upsert입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메타데이터 구조: .hoodie/ Timeline&lt;/h4&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;&amp;lt;table_base_path&amp;gt;/
├── .hoodie/
│   ├── hoodie.properties          # 테이블 핵심 설정
│   ├── &amp;lt;instant&amp;gt;.commit           # 완료된 커밋
│   ├── &amp;lt;instant&amp;gt;.inflight         # 진행 중
│   ├── &amp;lt;instant&amp;gt;.requested        # 요청됨
│   ├── archived/                  # 아카이브된 타임라인
│   └── metadata/                  # Hudi Metadata Table (파일 목록, Bloom Filter, RLI)
└── partition_col=value/
    └── *.parquet / *.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timeline 파일명 자체가 순서를 표현하며, completed 상태 인스턴트만 Reader에 가시적입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카탈로그 아키텍처&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 카탈로그 서버 없음. HMS에 스키마/파티션 자동 싱크(HiveSyncTool). 경로 기반 직접 접근도 가능합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테이블 타입: COW vs MOR&lt;/h4&gt;
&lt;p&gt;특성 Copy-on-Write (COW) Merge-on-Read (MOR)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;업데이트 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;파일 전체 재작성&lt;/td&gt;
&lt;td&gt;.log 파일에 변경 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;읽기 성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;우수 (base file만)&lt;/td&gt;
&lt;td&gt;보통 (base + log 병합)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;쓰기 성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;높음 (log append)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;적합 케이스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;배치 중심, 변경 빈도 낮음&lt;/td&gt;
&lt;td&gt;CDC/스트리밍, 고빈도 upsert&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ACID 트랜잭션: NBCC (Hudi 1.0의 핵심)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기본 OCC&lt;/b&gt;: 파일 그룹 단위 충돌 감지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NBCC (Non-Blocking Concurrency Control)&lt;/b&gt;: 여러 스트리밍 Writer가 동일 File Slice에 Log File을 &lt;b&gt;자유롭게 병렬 기록&lt;/b&gt;, 충돌 해소를 Compaction 단계로 위임. 명시적 락 불필요&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Walmart 사례&lt;/b&gt;: 대규모 뮤터블 워크로드에서 Delta/Iceberg OCC가 반복 실패한 반면, Hudi NBCC만 안정적으로 동작했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Upsert 특화: Record-Level Index&lt;/h4&gt;
&lt;p&gt;인덱스 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Bloom Filter&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기본. 파일 footer에 저장, 2단계 프루닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HBase Index&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;O(1) 조회, 외부 HBase 서버 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Bucket Index&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hash 기반 결정론적 라우팅, 인덱스 조회 불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Record-Level Index (RLI, 0.14+)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;외부 서버 없이 HBase 수준 성능. 현재 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Clustering 알고리즘&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hudi는 &lt;b&gt;Linear Sort, Z-order, Hilbert Curve&lt;/b&gt; 모두 지원합니다. 인라인(write 시 동시) 또는 비동기(별도 서비스) 두 모드 선택 가능하며 파티션 내 fine-grained clustering이 가능합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 기능&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Incremental Query&lt;/b&gt;: 특정 시점 이후 변경된 레코드만 조회 (설계 초기부터, Delta CDF보다 앞서 구현)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CDC Query (0.13+)&lt;/b&gt;: before/after 이미지 + 작업 유형(insert/update/delete) 포함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 타임 정렬&lt;/b&gt;: RecordPayload / RecordMerger API로 늦게 도착한 데이터도 정확하게 병합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hudi Metadata Table&lt;/b&gt;: 파일 목록, Bloom Filter, 컬럼 통계, 레코드 인덱스 캐싱 &amp;rarr; S3 LIST 비용 절감&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LSM Timeline (1.0)&lt;/b&gt;: 수백만 히스토리 인스턴트를 LSM Tree 방식으로 효율 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HMS 강한 의존성, Unity Catalog 같은 통합 거버넌스 부재&lt;/li&gt;
&lt;li&gt;MOR 테이블은 주기적 Compaction 없이 읽기 성능 저하&lt;/li&gt;
&lt;li&gt;Snowflake, BigQuery 등 클라우드 DW에서 네이티브 지원 미흡&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Apache Paimon&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Iceberg와 유사한 스냅샷 기반 구조를 가지나, &lt;b&gt;스트리밍 네이티브 설계&lt;/b&gt;와 &lt;b&gt;LSM Tree 저장 구조&lt;/b&gt;가 핵심 차별점입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flink Table Store로 출발(2022) &amp;rarr; Apache Paimon으로 개명 후 ASF 독립 프로젝트(2023).&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메타데이터 구조: 이중 Manifest List&lt;/h4&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;Snapshot
  ├── Base Manifest List   &amp;larr; S-1까지 누적 뷰 (배치 리더용)
  └── Delta Manifest List  &amp;larr; 직전 스냅샷 대비 변경분만 (스트리밍 리더용)
        └── Manifest File (Avro)
              └── Data File / Changelog File / Index File
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트리밍 리더는 Delta Manifest만 읽어 최신 변경분만 소비, 배치 쿼리는 Base + Delta 결합으로 전체 뷰를 얻습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카탈로그 아키텍처&lt;/h4&gt;
&lt;p&gt;카탈로그 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;FilesystemCatalog&lt;/b&gt; (기본)&lt;/td&gt;
&lt;td&gt;외부 서비스 불필요. 파일 어휘 정렬로 최신 스냅샷 파악&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HiveCatalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hive에서 직접 접근 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;JdbcCatalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MySQL/PostgreSQL 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;REST Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;표준 REST 인터페이스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;LSM Tree 기반 저장&lt;/h4&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Write &amp;rarr; Memory Buffer (Primary Key 기준 정렬)
     &amp;rarr; Flush (Level 0 Sorted Runs)
     &amp;rarr; Background Compaction &amp;rarr; Level 1, 2, ...N
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Primary Key Table&lt;/b&gt;: CRUD 완전 지원. Merge Engine: Deduplicate / Partial Update / Aggregation / First Row&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Append-Only Table&lt;/b&gt;: INSERT 전용. 메시지 큐 대체용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Flink 통합 및 스트리밍 차별점&lt;/h4&gt;
&lt;p&gt;항목 Iceberg Paimon&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스트리밍 레이턴시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음 (마이크로배치)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;100ms 미만&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Changelog 생성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;없음 (별도 CDC 툴 필요)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;내장 Changelog Producer&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Flink 통합&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;커넥터 수준&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네이티브&lt;/b&gt; (Materialized Tables, 2-Phase Commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;워터마크&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;내장 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Changelog Producer 모드&lt;/b&gt;: input (CDC 소스 그대로) &amp;middot; lookup (before/after 이미지 생성) &amp;middot; full-compaction (안정성 우선)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Exactly-once 처리 보장&lt;/b&gt;: Paimon은 Flink의 &lt;b&gt;체크포인트 메커니즘과 완벽하게 연동&lt;/b&gt;됩니다. Flink 작업 장애 복구 시 중복 쓰기나 데이터 유실 없이 &lt;b&gt;exactly-once semantics&lt;/b&gt;를 보장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Flink 외부 상태 저장소로 활용&lt;/b&gt;: Paimon Primary Key Table을 Flink 스트리밍 작업의 &lt;b&gt;외부 상태 저장소&lt;/b&gt;로 사용할 수 있습니다. Kafka 소비 오프셋 관리와 연동하여 체크포인트 기반 일관된 복구가 가능합니다. (Fluss PK Table이 이 역할을 더 최적화하는 방향으로 진화 중)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Iceberg 호환성&lt;/h4&gt;
&lt;pre class=&quot;sml&quot;&gt;&lt;code&gt;'metadata.iceberg.storage' = 'hadoop-catalog'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Iceberg v3 Deletion Vector 지원 시 Paimon Primary Key Table을 Iceberg 리더로 완전히 읽기 가능합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Small File 자동화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flink 스트리밍 파이프라인 안에서 LSM compaction이 &lt;b&gt;상시 자동 실행&lt;/b&gt;됩니다. 별도 compaction 스케줄러가 필요 없습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상대적으로 신생 프로젝트 (커뮤니티 성숙도 낮음)&lt;/li&gt;
&lt;li&gt;Flink 중심 생태계 (Spark/Trino에서 일부 기능 제한)&lt;/li&gt;
&lt;li&gt;배치 처리 성능은 Iceberg/Delta 대비 낮을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SQL DB 기반 포맷&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. DuckLake&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025~2026년 가장 주목받는 신흥 포맷. &lt;b&gt;&quot;파일 기반 메타데이터&quot; 패러다임과 완전히 다른 축&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배경&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2025년 5월: DuckDB Labs가 DuckLake v0.1 발표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2026년 4월 13일: v1.0 production-ready 출시&lt;/b&gt; (DuckDB v1.5.2 내장)&lt;/li&gt;
&lt;li&gt;DuckDB 핵심 확장 중 다운로드 Top-10 진입, 수십 개 기업 프로덕션 사용 중&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;핵심 아이디어: SQL DB가 카탈로그 + 메타데이터&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 포맷의 &quot;Iceberg + Polaris 카탈로그&quot; 조합 전체를 &lt;b&gt;SQL DB 하나로 대체&lt;/b&gt;합니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;기존 패러다임:
  오브젝트 스토리지 파일들 + 외부 카탈로그 서비스 (HMS/Polaris)

DuckLake 패러다임:
  SQL DB (SQLite/PostgreSQL/DuckDB) &amp;larr; 카탈로그 + 메타데이터 모두 여기
  오브젝트 스토리지 &amp;larr; 실제 Parquet 데이터 파일만
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;항목 Delta/Iceberg/Hudi DuckLake&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메타데이터 저장&lt;/td&gt;
&lt;td&gt;JSON 로그/Avro (object storage)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SQL DB 테이블&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;카탈로그&lt;/td&gt;
&lt;td&gt;별도 Hive Metastore / REST 카탈로그&lt;/td&gt;
&lt;td&gt;SQL DB 자체가 카탈로그&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 파일&lt;/td&gt;
&lt;td&gt;Parquet / ORC&lt;/td&gt;
&lt;td&gt;Parquet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동시성 제어&lt;/td&gt;
&lt;td&gt;OCC (파일 레벨)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SQL DB 트랜잭션&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Data Inlining: Small File 문제 원천 해소&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소량 변경(수십~수백 행)을 Parquet으로 저장하지 않고 &lt;b&gt;카탈로그 SQL DB에 직접 인라인 저장&lt;/b&gt;합니다. 누적되면 CHECKPOINT 명령으로 Parquet으로 플러시합니다. 소규모 스트리밍 쓰기에서 소파일 폭발 문제를 원천적으로 회피합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메타데이터 규모&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1PB 데이터 기준 메타데이터 크기 약 &lt;b&gt;10GB&lt;/b&gt; 수준. SQL DB가 처리하기에 적합한 크기입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;지원 클라이언트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DuckDB 외에도 Apache DataFusion, Apache Spark, Trino, Pandas 클라이언트 구현이 완료되었습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점 &amp;amp; 주의사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SQL DB가 단일 장애점(SPOF)&lt;/b&gt;: PostgreSQL 등 외부 DB에 의존 &amp;mdash; 순수 object-storage 아키텍처를 선호하는 팀에 부적합&lt;/li&gt;
&lt;li&gt;커뮤니티 성숙도 낮음 (생태계 초기 단계)&lt;/li&gt;
&lt;li&gt;멀티 엔진 대규모 동시성에서의 검증 부족&lt;/li&gt;
&lt;li&gt;파티션 진화, 스키마 진화 기능이 기존 포맷 대비 단순&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;적합한 케이스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DuckDB 중심 소규모 팀 / 단일 엔진 환경&lt;/li&gt;
&lt;li&gt;로컬 또는 소규모 클라우드 분석 워크로드&lt;/li&gt;
&lt;li&gt;운영 복잡도를 최소화하고 싶은 경우&lt;/li&gt;
&lt;li&gt;기존 SQL 인프라(PostgreSQL 등)를 메타데이터 저장소로 재활용하고 싶은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스트리밍 스토리지 레이어 (Hot Tier)&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Apache Fluss&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[스트리밍 스토리지 &amp;mdash; Lakehouse 테이블 포맷 아님]&lt;/b&gt; Fluss는 Delta Lake/Iceberg/Hudi/Paimon 같은 레이크하우스 파일 포맷이 아닙니다. 분산 스트리밍 스토리지 시스템으로, Paimon/Iceberg의 &lt;b&gt;hot tier 보완재&lt;/b&gt;입니다. Kafka + RocksDB의 schematized 후계자에 가깝습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배경 및 거버넌스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;출처&lt;/b&gt;: Alibaba/Ververica &amp;rarr; Flink Forward Asia 2024 오픈소스 공개&lt;/li&gt;
&lt;li&gt;&lt;b&gt;거버넌스&lt;/b&gt;: Apache Software Foundation Incubating (2025년 6월)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이선스&lt;/b&gt;: Apache 2.0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최신 버전&lt;/b&gt;: v0.9.0 (2026년 3월)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 규모 (Taobao)&lt;/b&gt;: 3PB+, 40 GB/s ingest, 500K QPS 포인트 룩업, 단일 테이블 500B+ rows&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클러스터 아키텍처&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[Flink Job / Spark Client]
        │
        ▼
CoordinatorServer ── 메타데이터 관리, Tablet 할당, 리밸런싱, Tiering 조정
        │
        ▼
TabletServer (N개)
  ├── LogStore (Apache Arrow IPC Columnar)
  │     &amp;rarr; append-only, Kafka 스타일 복제, 서버 측 Column Pruning
  └── KvStore (RocksDB)
        &amp;rarr; PK 기반 mutable 저장소, 500K QPS 포인트 룩업
        &amp;rarr; CDC Changelog 출력 (+I / +U / -U / -D)
ZooKeeper ── 분산 조정 (향후 KvStore로 대체 예정)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일 기반 포맷과의 핵심 차이&lt;/b&gt;: 파일 + 메타데이터 규약이 아닌 &lt;b&gt;서버 클러스터가 필수&lt;/b&gt;인 분산 시스템입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;데이터 포맷 이중 구조&lt;/h4&gt;
&lt;p&gt;계층 포맷 저장 위치 레이턴시&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;핫 (Fluss 직접)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Arrow IPC (열 기반, zero-copy)&lt;/td&gt;
&lt;td&gt;TabletServer NVMe/SSD&lt;/td&gt;
&lt;td&gt;밀리초 미만 (sub-second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;콜드 (Tiering 이후)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parquet (Paimon / Iceberg / Lance)&lt;/td&gt;
&lt;td&gt;S3/OSS 객체 스토리지&lt;/td&gt;
&lt;td&gt;분~시간 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테이블 타입&lt;/h4&gt;
&lt;p&gt;타입 특징 적합 사용처&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Log Table&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;append-only, schematized Kafka topic. Arrow IPC 저장&lt;/td&gt;
&lt;td&gt;이벤트 스트림, 메시지 큐 대체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Primary Key (PK) Table&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RocksDB KvStore + WAL. CRUD + CDC Changelog 출력 (+I/+U/-U/-D)&lt;/td&gt;
&lt;td&gt;실시간 차원 테이블, A/B 카운터, CDC 핫 경로, Lookup Join 외부화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Union Read &amp;mdash; 핫&amp;middot;콜드 통합 쿼리&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 핫(Fluss) + 콜드(Paimon/Iceberg) 통합 조회
SELECT * FROM fluss_table;

-- 콜드(Paimon/Iceberg) 전용 &amp;mdash; $lake suffix
SELECT * FROM fluss_table$lake;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flink SQL에서 hot/cold 데이터를 자동 통합합니다. Tiering Service가 백그라운드에서 Fluss &amp;rarr; Paimon/Iceberg로 데이터를 자동 이관합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;엔진 지원&lt;/h4&gt;
&lt;p&gt;엔진 지원 수준&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Flink&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1급 네이티브 (설계 1순위)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Spark&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;v0.9부터 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Trino / StarRocks&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로드맵 단계 (Union Read 포함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon / Iceberg 클라이언트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Tiering 후 표준 방식으로 자동 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Fluss vs Paimon vs Kafka 비교&lt;/h4&gt;
&lt;p&gt;항목 Fluss Paimon Kafka&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;본질&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;분산 스트리밍 스토리지 시스템&lt;/td&gt;
&lt;td&gt;레이크하우스 파일 포맷&lt;/td&gt;
&lt;td&gt;분산 메시지 큐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;쓰기 레이턴시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;밀리초 미만&lt;/td&gt;
&lt;td&gt;초~분 단위&lt;/td&gt;
&lt;td&gt;밀리초 미만&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스키마 강제&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;예 (강한 타입)&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;td&gt;아니오 (기본)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PK/Upsert&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;예 (RocksDB, 500K QPS)&lt;/td&gt;
&lt;td&gt;예 (LSM Tree)&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Time Travel&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적 (cold 계층 의존)&lt;/td&gt;
&lt;td&gt;예 (스냅샷)&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OLAP 직접 쿼리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Tiering 후 가능&lt;/td&gt;
&lt;td&gt;예 (Spark/Trino)&lt;/td&gt;
&lt;td&gt;아니오&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서버 클러스터 필요&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예&lt;/b&gt; (CoordinatorServer + TabletServer)&lt;/td&gt;
&lt;td&gt;아니오 (라이브러리)&lt;/td&gt;
&lt;td&gt;예&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;핫 데이터 포맷&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Arrow IPC&lt;/td&gt;
&lt;td&gt;없음 (파일 직접)&lt;/td&gt;
&lt;td&gt;바이너리 로그&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카탈로그 아키텍처&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 분산 카탈로그 (CoordinatorServer + ZooKeeper). Hive Metastore, Glue, Polaris 같은 파일 기반 외부 카탈로그와 다른 모델입니다. Tiering 시 Iceberg/Paimon 카탈로그에 메타데이터를 자동 등록합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점 &amp;amp; 주의사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;서버 클러스터 필수&lt;/b&gt;: 파일 기반 포맷 대비 운영 복잡도 높음 (k8s 배포 필요)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flink 중심 생태계&lt;/b&gt;: Spark(0.9 신규), Trino/StarRocks(로드맵) &amp;mdash; 성숙도 제한&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ASF Incubating 단계&lt;/b&gt;: v0.9, 아직 안정화 진행 중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Time Travel 약함&lt;/b&gt;: 사용자 대면 Time Travel은 Paimon/Iceberg에 의존&lt;/li&gt;
&lt;li&gt;&lt;b&gt;채택 사례 편중&lt;/b&gt;: 대부분 Alibaba 그룹 (외부 기업 공개 사례 제한적)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;적합한 케이스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sub-second freshness가 요구되는 실시간 대시보드 및 서비스&lt;/li&gt;
&lt;li&gt;Flink Lookup Join 외부 상태 저장소 (Fluss PK Table)&lt;/li&gt;
&lt;li&gt;CDC 핫 경로 처리 후 Tiering으로 자동 배치 분석 연동&lt;/li&gt;
&lt;li&gt;실시간 개인화 추천, A/B 테스트 카운터, 실시간 특성 저장소&lt;/li&gt;
&lt;li&gt;Kafka에 스키마 강제 + OLAP 쿼리 연동이 동시에 필요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상호운용 레이어&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Apache XTable (구 OneTable)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 포맷이 아니라 &lt;b&gt;포맷 간 메타데이터 변환 레이어&lt;/b&gt;입니다. 데이터 파일 복사 없이 메타데이터만 변환합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2024년 2월: Apache Incubating 프로젝트 편입&lt;/li&gt;
&lt;li&gt;Delta Lake &amp;harr; Apache Iceberg &amp;harr; Apache Hudi 간 &lt;b&gt;양방향 변환&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Paimon 지원 로드맵 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Delta UniForm과의 차이&lt;/h4&gt;
&lt;p&gt;항목 Delta UniForm Apache XTable&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주체&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Databricks (Delta Lake 내장)&lt;/td&gt;
&lt;td&gt;독립 오픈소스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;방향&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta &amp;rarr; Iceberg/Hudi (단방향 중심)&lt;/td&gt;
&lt;td&gt;모든 포맷 간 양방향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;의존성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta Lake 필수&lt;/td&gt;
&lt;td&gt;포맷 무관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;거버넌스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Databricks 주도&lt;/td&gt;
&lt;td&gt;ASF 중립&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;적합한 케이스&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 Delta Lake 테이블을 Iceberg 기반 툴에서도 읽어야 하는 경우&lt;/li&gt;
&lt;li&gt;포맷 전환 없이 멀티 포맷 환경을 운영해야 하는 경우&lt;/li&gt;
&lt;li&gt;특정 벤더에 종속 없이 포맷 상호운용성을 유지하고 싶은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;심화 비교&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Deletion Vector 구현 방식 비교&lt;/h3&gt;
&lt;p&gt;포맷 방식 저장 위치 특이사항&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Compressed Bitmap (3.1+)&lt;/td&gt;
&lt;td&gt;_delta_log/*.bin 사이드카&lt;/td&gt;
&lt;td&gt;REORG로 물리 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg v3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Puffin 파일 + Compressed Bitmap&lt;/td&gt;
&lt;td&gt;데이터 파일 옆 sidecar&lt;/td&gt;
&lt;td&gt;파일당 DV 1개 제한, v2 대비 삭제 55% 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg v2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Position Delete File (행 위치) + Equality Delete File (값 기반)&lt;/td&gt;
&lt;td&gt;별도 Parquet 파일&lt;/td&gt;
&lt;td&gt;파일 폭발 문제 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta Log File (append-only)&lt;/td&gt;
&lt;td&gt;.log 파일&lt;/td&gt;
&lt;td&gt;이벤트 타임 정렬 지원이 강점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LSM multi-level compaction&lt;/td&gt;
&lt;td&gt;LSM 구조 자체&lt;/td&gt;
&lt;td&gt;별도 DV 없음, LSM이 삭제/갱신 흡수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Iceberg v3 + Delta가 유사한 DV 방식으로 수렴 중. Hudi는 이벤트 타임 처리에서 차별화. Paimon은 LSM 구조 자체가 DV 역할.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Small File 문제 해결 전략&lt;/h3&gt;
&lt;p&gt;포맷 발생 원인 해결 메커니즘 자동화 수준&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스트리밍 micro-batch마다 파일 생성&lt;/td&gt;
&lt;td&gt;OPTIMIZE 수동 실행 + Auto Optimize (Databricks 전용)&lt;/td&gt;
&lt;td&gt;낮음 (OSS) / 높음 (Databricks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스냅샷 기반 쓰기로 파일 누적&lt;/td&gt;
&lt;td&gt;rewrite_data_files 수동 트리거&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MOR log 파일 누적&lt;/td&gt;
&lt;td&gt;파일 그룹 단위 비동기 compaction (writer와 독립)&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스트리밍 flush마다 소규모 SST 파일 생성&lt;/td&gt;
&lt;td&gt;LSM compaction이 Flink 파이프라인 내 &lt;b&gt;상시 자동 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;높음&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckLake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;소량 변경&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Data Inlining&lt;/b&gt; (SQL DB에 직접 저장 후 CHECKPOINT)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;원천 회피&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 레이아웃 최적화 (클러스터링)&lt;/h3&gt;
&lt;p&gt;포맷 알고리즘 자동화 비고&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Hilbert Curve&lt;/b&gt; (Liquid Clustering)&lt;/td&gt;
&lt;td&gt;Databricks 전용 Auto&lt;/td&gt;
&lt;td&gt;Z-ORDER는 레거시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Z-order&lt;/td&gt;
&lt;td&gt;수동&lt;/td&gt;
&lt;td&gt;Hilbert Curve 미지원 (2026 기준)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Linear Sort, Z-order, &lt;b&gt;Hilbert Curve&lt;/b&gt; 모두&lt;/td&gt;
&lt;td&gt;인라인 또는 비동기&lt;/td&gt;
&lt;td&gt;알고리즘 선택 폭 가장 넓음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Primary Key 기준 자동 정렬 (LSM)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;자동&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;별도 clustering 명령 불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동시 다중 Writer 지원&lt;/h3&gt;
&lt;p&gt;포맷 메커니즘 특이사항&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;OCC &amp;mdash; metadata.json atomic swap&lt;/td&gt;
&lt;td&gt;충돌 시 rollback + retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;OCC + 비겹침 파일 변경 동시 허용&lt;/td&gt;
&lt;td&gt;Coordinated Commits (4.0)로 클라우드 환경 보강&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;OCC + &lt;b&gt;NBCC&lt;/b&gt; (1.0)&lt;/td&gt;
&lt;td&gt;TrueTime 기반 completion-time ordering. 명시적 락 없이 여러 writer 동시 진행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Snapshot isolation + 버킷 레벨 writer 제한&lt;/td&gt;
&lt;td&gt;DV 모드에서 버킷 내 병렬 읽기 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hudi NBCC&lt;/b&gt; 가장 유리 &amp;mdash; 대규모 스트리밍 upsert 파이프라인에서 lock contention 없이 다중 writer 동시 실행&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오픈소스 거버넌스 &amp;amp; 벤더 독립성&lt;/h3&gt;
&lt;p&gt;포맷 거버넌스 실질 주도 벤더 독립성&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Software Foundation&lt;/td&gt;
&lt;td&gt;Netflix, Apple, AWS, Snowflake 등 30개+ 기업 공동&lt;/td&gt;
&lt;td&gt;&lt;b&gt;최고&lt;/b&gt; &amp;mdash; 클라우드 3사 모두 네이티브 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Hudi&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Software Foundation&lt;/td&gt;
&lt;td&gt;Onehouse(구 Uber 팀) 주도&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Paimon&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Software Foundation&lt;/td&gt;
&lt;td&gt;Alibaba/Ververica 주도&lt;/td&gt;
&lt;td&gt;중간 (Flink 의존성)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Linux Foundation&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Databricks 코드 기여 대부분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간 &amp;mdash; OSS에 없는 Databricks 전용 기능 다수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckLake&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DuckDB Labs&lt;/td&gt;
&lt;td&gt;DuckDB Labs&lt;/td&gt;
&lt;td&gt;중간 (DuckDB 의존)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Delta Lake Databricks 전용 기능&lt;/b&gt; (OSS에 없음): Auto Optimize &amp;middot; Auto Loader &amp;middot; Bloom Filter Index &amp;middot; Primary Key 제약&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;카탈로그 Federation &amp;amp; 글로벌 메타데이터 서비스&lt;/h3&gt;
&lt;p&gt;서비스 역할 지원 포맷&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&quot;Catalog of Catalogs&quot; &amp;mdash; Hive, Iceberg REST, Kafka Schema Registry 통합&lt;/td&gt;
&lt;td&gt;Delta, Iceberg, Hudi, Paimon 1등급 목표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Git 스타일 브랜치/태그, &lt;b&gt;멀티 테이블 트랜잭션&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg 주력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg REST 표준 구현&lt;/td&gt;
&lt;td&gt;Iceberg 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Unity Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta 네이티브 + 외부 Iceberg 테이블 지원. RLS/Column Masking&lt;/td&gt;
&lt;td&gt;Delta 네이티브&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Lake Formation&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Tag 기반 ABAC, 행/열 수준 접근 제어&lt;/td&gt;
&lt;td&gt;Iceberg, Delta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;멀티 테이블 트랜잭션&lt;/b&gt;: 포맷 자체 스펙에는 없음. &lt;b&gt;Project Nessie + Iceberg&lt;/b&gt;가 현재 가장 성숙한 크로스 테이블 원자 커밋 솔루션입니다. (Branch &amp;rarr; 여러 테이블 변경 &amp;rarr; Merge 패턴)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;카탈로그 의존성 핵심 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Iceberg &amp;mdash; 카탈로그가 필수인 이유&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[카탈로그 없이]
  v1.metadata.json  &amp;larr; 구버전?
  v2.metadata.json  &amp;larr; 최신 버전? 어느 것이 현재인지 알 수 없음
  v3.metadata.json  &amp;larr; 충돌로 중복 생성?

[카탈로그 있을 때]
  Catalog: orders &amp;rarr; v2.metadata.json  &amp;larr; 원자적 CAS로 관리되는 단일 진실 공급원
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Delta Lake &amp;mdash; 카탈로그 없이 동작 가능한 이유&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;_delta_log/
  00000000000000000000.json  # 버전 0
  00000000000000000001.json  # 버전 1
  00000000000000000010.parquet  # 체크포인트
  _last_checkpoint  # &quot;최신 체크포인트는 버전 10&quot; 명시

&amp;rarr; 파일 번호 자체가 순서를 보장, 별도 포인터 불필요
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Hudi &amp;mdash; Delta Lake와 유사&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;.hoodie/
  20250101000000.commit   # 완료됨
  20250101000100.commit   # 완료됨
  20250101000200.inflight # 진행 중

&amp;rarr; 파일명 자체가 순서 표현, inflight/completed로 원자성 보장
&amp;rarr; HMS에 스키마 싱크하지만 테이블 자체는 독립 동작 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Paimon &amp;mdash; FilesystemCatalog로 외부 서비스 없이 동작&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;FilesystemCatalog:
  파일시스템 내 파일의 어휘 정렬로 최신 스냅샷 파악
  &amp;rarr; Iceberg의 Hadoop Catalog와 유사한 개념
  &amp;rarr; 외부 서비스 없이 동작 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. DuckLake &amp;mdash; SQL DB가 카탈로그 자체&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SQL DB (SQLite / PostgreSQL):
  테이블명 &amp;rarr; 스냅샷 ID &amp;rarr; 파일 목록 매핑 모두 SQL 테이블로 관리
  &amp;rarr; SQL ACID 트랜잭션으로 원자성 보장
  &amp;rarr; 외부 파일 기반 카탈로그 불필요
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Fluss &amp;mdash; 자체 분산 카탈로그 (CoordinatorServer + ZooKeeper)&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;CoordinatorServer + ZooKeeper (자체 분산 카탈로그):
  테이블 메타데이터, Tablet 위치, Tiering 상태를 직접 관리
  &amp;rarr; Hive/Glue/Polaris 같은 파일 기반 카탈로그와 전혀 다른 모델
  &amp;rarr; Flink Job이 CoordinatorServer에 직접 연결하여 메타데이터 조회
  &amp;rarr; Tiering 시 Iceberg/Paimon 카탈로그에 메타데이터 자동 등록
  &amp;rarr; 서버 클러스터가 카탈로그 역할 통합 (ZooKeeper 향후 KvStore로 대체 예정)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포맷 혼합 사용 아키텍처 패턴&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 포맷보다 &lt;b&gt;두 가지 포맷을 역할에 따라 분리&lt;/b&gt;해 사용하는 패턴이 2024~2025년부터 급증하고 있습니다. 실시간 수집의 강점과 배치 분석 생태계를 동시에 활용하는 것이 핵심 동기입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;혼합 패턴 비교&lt;/h3&gt;
&lt;p&gt;혼합 패턴 주요 채택 기업 핵심 동기 브리징 수단 운영 복잡도&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Paimon + Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;알리바바, ByteDance, Bondex&lt;/td&gt;
&lt;td&gt;Flink 실시간 + 멀티엔진 분석&lt;/td&gt;
&lt;td&gt;Paimon Iceberg 호환 스냅샷 (네이티브)&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi + Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Uber, Robinhood, Onehouse&lt;/td&gt;
&lt;td&gt;CDC/Upsert + 다중 엔진 분석&lt;/td&gt;
&lt;td&gt;Apache XTable (양방향 변환)&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta + Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Capital One, Databricks&lt;/td&gt;
&lt;td&gt;Spark 최적화 + 멀티클라우드&lt;/td&gt;
&lt;td&gt;Delta UniForm (단방향)&lt;/td&gt;
&lt;td&gt;낮음~중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Fluss + Paimon + Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;알리바바/Ververica&lt;/td&gt;
&lt;td&gt;핫-웜-콜드 3계층 Streamhouse&lt;/td&gt;
&lt;td&gt;Flink SQL Union Read&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckLake + Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;MotherDuck 생태계&lt;/td&gt;
&lt;td&gt;로컬 처리 + 중앙 공유 분석&lt;/td&gt;
&lt;td&gt;Iceberg 메타데이터 임포트&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Paimon + Iceberg &amp;mdash; 가장 주목받는 조합&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 사례&lt;/b&gt;: 알리바바(Taobao/Tmall, 수백 PB), ByteDance/TikTok, Bondex, StreamNative&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 포맷의 강점이 명확하게 분리됩니다.&lt;/p&gt;
&lt;p&gt;역할 Paimon Iceberg&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설계 철학&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스트리밍 우선, LSM Tree&lt;/td&gt;
&lt;td&gt;배치 분석 우선, 불변 스냅샷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;갱신 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;초단위 CDC/Upsert (LSM 흡수)&lt;/td&gt;
&lt;td&gt;Copy-on-Write (배치 최적화)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Flink 통합&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;네이티브 1급 지원&lt;/td&gt;
&lt;td&gt;커넥터 수준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;분석 생태계&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;성장 중&lt;/td&gt;
&lt;td&gt;Spark, Trino, Snowflake, BigQuery 폭넓게 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 흐름:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Kafka / Flink CDC
        │
        ▼
[Flink + Paimon]  ─── 실시간 수집 레이어 (초~분 단위 freshness)
   LSM Tree              ├── Lookup Join / 실시간 OLAP (StarRocks, Doris)
   Changelog Producer    └── Flink SQL 집계 / 대시보드
        │
        │ Paimon Iceberg 호환 스냅샷 자동 생성
        │ (metadata.iceberg.storage = 'rest-catalog')
        ▼
[Iceberg REST Catalog]  ─── 배치 분석 레이어 (시간~일 단위)
   불변 스냅샷                 ├── Spark 대용량 배치 집계
   Time Travel                ├── Trino / Athena 다중 엔진
   Hidden Partitioning        └── Snowflake / BigQuery 분석 서비스
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브리징 메커니즘&lt;/b&gt;: Paimon 테이블에 metadata.iceberg.storage = 'rest-catalog' 설정 시 쓰기 커밋마다 Iceberg 메타데이터를 REST Catalog에 자동 등록합니다. Iceberg 클라이언트는 동일 Parquet 파일을 Iceberg 테이블로 인식합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의사항&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 대형 테이블은 신규 커밋 전까지 Iceberg 클라이언트에 비가시 (lazy generation)&lt;/li&gt;
&lt;li&gt;Paimon 메타데이터 + Iceberg 메타데이터 이중 유지 필요&lt;/li&gt;
&lt;li&gt;LSM compaction 타이밍과 Iceberg 스냅샷 생성 타이밍 별도 튜닝 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Hudi + Iceberg &amp;mdash; CDC 특화 수집 + 멀티엔진 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 사례&lt;/b&gt;: Uber(Hudi 창시, CDC 수집), Robinhood(Kafka&amp;rarr;Hudi&amp;rarr;Iceberg), Onehouse(XTable 기반 다중 포맷)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 흐름:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;MySQL / PostgreSQL / MongoDB
        │ (Debezium CDC)
        ▼
    Kafka Topics
        │ (Hudi DeltaStreamer)
        ▼
[Hudi MOR Tables]  ─── 수집 레이어 (분 단위 freshness)
   Record-level Upsert      ├── 실시간 조회 (Read Optimized View)
   NBCC 다중 Writer          ├── CDC 히스토리 보존
   이벤트 타임 정렬          └── 증분 처리 파이프라인
        │
        │ Apache XTable (메타데이터 양방향 변환)
        │ (실제 Parquet 파일 복사 없음)
        ▼
[Iceberg Tables]  ─── 분석 레이어
   COW 최적화                 ├── Spark 대용량 배치 분석
   파티션 진화                ├── Trino / Presto OLAP
   Time Travel               ├── Snowflake / BigQuery 외부 테이블
                             └── Dremio 셀프서비스 분석
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Apache XTable 브리징&lt;/b&gt;: 소스 포맷의 메타데이터를 읽어 타겟 포맷의 메타데이터를 재생성합니다. Parquet 데이터 파일은 복사 없이 메타데이터만 변환됩니다. Hudi &amp;harr; Iceberg &amp;harr; Delta 3방향 양방향 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Delta UniForm과의 차이&lt;/b&gt;: Delta UniForm은 Delta &amp;rarr; Iceberg 단방향이며 Databricks 종속. XTable은 완전 양방향, 벤더 중립입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Delta + Iceberg &amp;mdash; Spark 최적화 + 멀티클라우드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 사례&lt;/b&gt;: Capital One(Lakehouse Convergence 패턴 공개), Databricks(2024년 Tabular 인수 후 Unity Catalog에서 Delta+Iceberg 동시 지원)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 흐름:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;Spark Structured Streaming / Batch
        │
        ▼
[Delta Lake]  ─── Spark 처리 레이어
   _delta_log/           ├── ETL/ELT 변환 (Databricks)
   Auto-Optimize         ├── ML/AI 워크플로우
   ACID 트랜잭션          └── Delta Live Tables
        │
        │ Delta UniForm (Delta 커밋 시 Iceberg 메타데이터 자동 생성)
        │ 또는 Apache XTable (양방향 변환)
        ▼
[Iceberg 메타데이터 레이어]  ─── 멀티엔진 분석
   (동일 Parquet 파일 공유)      ├── AWS Athena / Glue
   Time Travel                  ├── Snowflake External Tables
   Partition Pruning            ├── Google BigQuery Iceberg
                                └── Trino / Dremio
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의사항&lt;/b&gt;: Delta UniForm은 단방향(Delta &amp;rarr; Iceberg 읽기 전용). Iceberg 엔진에서 쓰기 불가. 최적 경험을 위해 Unity Catalog 의존성이 생깁니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Streamhouse: Fluss + Paimon + Iceberg (핫-웜-콜드 3계층)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 사례&lt;/b&gt;: 알리바바/Ververica &amp;mdash; &quot;From Kappa Architecture to Streamhouse&quot; (2025)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 주목받는 신규 아키텍처 패턴입니다. 단순 레이크하우스를 넘어 실시간 스트리밍과 배치 분석을 통합하는 Streamhouse 개념을 구현합니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;데이터 소스 &amp;rarr; Kafka
        │
        ▼
[핫 레이어: Fluss]  ─── 초~분 단위 freshness
   스트리밍 스토리지       ├── Flink 실시간 처리
   Changelog 네이티브     ├── 실시간 집계/조인
   낮은 레이턴시           └── 이벤트 시간 처리
        │ (Fluss &amp;rarr; Paimon 자동 싱크)
        ▼
[웜 레이어: Paimon]  ─── 분~시간 단위 freshness
   LSM Tree 갱신          ├── OLAP 엔진 쿼리 (Doris, StarRocks)
   Iceberg 호환 스냅샷    ├── Flink Lookup Join
   배치 처리 가능          └── 중기 데이터 보존
        │ (Paimon Iceberg 호환 스냅샷)
        ▼
[콜드 레이어: Iceberg]  ─── 시간~일 단위 freshness
   불변 스냅샷              ├── Spark 대용량 배치
   멀티엔진 접근           ├── Trino / Athena
   장기 보존               └── Snowflake / BigQuery
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flink SQL에서 UNION ALL로 핫&amp;middot;웜&amp;middot;콜드 레이어를 통합 쿼리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메달리온 아키텍처에서의 레이어별 포맷 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024~2025년 기준 메달리온 아키텍처(Bronze &amp;rarr; Silver &amp;rarr; Gold)에서 레이어별 포맷을 달리 사용하는 패턴이 증가하고 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────┐
│  Gold Layer (비즈니스 집계, BI/분석 소비 준비)            │
│  &amp;rarr; Iceberg 또는 Delta  (멀티엔진, BI 도구 연동)          │
│  &amp;rarr; 소규모 고활용 테이블, 파티션 최적화                   │
├──────────────────────────────────────────────────────────┤
│  Silver Layer (정제, 표준화, 도메인 모델)                 │
│  &amp;rarr; Iceberg  (스키마 진화, Time Travel, 검증)             │
│  &amp;rarr; Hudi  (CDC 소스의 경우 Upsert 정합성 유지)            │
├──────────────────────────────────────────────────────────┤
│  Bronze Layer (원시 데이터, 수집 즉시 저장)               │
│  &amp;rarr; Paimon  (Flink 실시간 수집, 초단위 갱신)              │
│  &amp;rarr; Hudi MOR  (CDC 소스, 고처리량 upsert)                 │
│  &amp;rarr; Iceberg  (단순 Append-only, S3 Tables)                │
└──────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2025년 주류 구현 패턴 4가지:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;패턴 Bronze Silver/Gold 브리징 적합 조직&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;A. Hudi &amp;rarr; Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hudi MOR (CDC/Upsert)&lt;/td&gt;
&lt;td&gt;Iceberg (Spark 정제 + 집계)&lt;/td&gt;
&lt;td&gt;XTable 변환&lt;/td&gt;
&lt;td&gt;CDC 중심, 멀티엔진 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;B. Paimon &amp;rarr; Iceberg&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Paimon (Flink 실시간)&lt;/td&gt;
&lt;td&gt;Iceberg (Spark 정제 + 집계)&lt;/td&gt;
&lt;td&gt;Iceberg 호환 스냅샷&lt;/td&gt;
&lt;td&gt;Flink 중심 조직&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;C. Delta 단일 + UniForm&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta (Spark Streaming)&lt;/td&gt;
&lt;td&gt;Delta (Spark 처리)&lt;/td&gt;
&lt;td&gt;Gold에만 UniForm 적용&lt;/td&gt;
&lt;td&gt;Databricks 전용 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;D. Iceberg 단일 포맷&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg (Append-only)&lt;/td&gt;
&lt;td&gt;Iceberg (Spark/Flink 정제)&lt;/td&gt;
&lt;td&gt;불필요&lt;/td&gt;
&lt;td&gt;멀티클라우드, 벤더 중립 우선&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2025년 트렌드&lt;/b&gt;: AWS S3 Tables &amp;middot; Snowflake &amp;middot; Google BigQuery &amp;middot; Azure 모두 Iceberg 네이티브 지원으로, Silver/Gold는 Iceberg로 통일하고 Bronze만 수집 특성에 맞는 포맷(Hudi/Paimon)을 쓰는 패턴이 급증하고 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 사례별 선택 가이드&lt;/h2&gt;
&lt;p&gt;사용 사례 권장 포맷 이유&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Databricks 기반 배치 ETL&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;가장 깊은 Databricks 통합, Unity Catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;멀티 엔진 (Spark + Trino + Flink + Snowflake)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;최고 수준 엔진 호환성, REST Catalog 표준, 벤더 중립&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;고빈도 CDC/Upsert 파이프라인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Hudi&lt;/td&gt;
&lt;td&gt;Record-level upsert 특화, NBCC 다중 Writer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Flink 기반 실시간 스트리밍&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Paimon&lt;/td&gt;
&lt;td&gt;스트리밍 네이티브, 100ms 미만 레이턴시, Flink 네이티브&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DuckDB 중심 소규모 분석&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DuckLake&lt;/td&gt;
&lt;td&gt;운영 단순성, SQL DB 기반 메타데이터, Data Inlining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Snowflake/BigQuery 연동&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;Snowflake Iceberg Tables, BigQuery BigLake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실시간 집계/부분 업데이트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Paimon&lt;/td&gt;
&lt;td&gt;Partial Update Merge Engine, Aggregation Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;멀티 테이블 원자 트랜잭션&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg + Nessie&lt;/td&gt;
&lt;td&gt;현재 유일하게 성숙한 크로스 테이블 원자 커밋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;포맷 혼용 환경 상호운용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache XTable&lt;/td&gt;
&lt;td&gt;데이터 복사 없이 포맷 간 메타데이터 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;동시 다중 스트리밍 Writer&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Hudi (NBCC)&lt;/td&gt;
&lt;td&gt;락 없는 다중 writer 동시 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;벤더 독립성 최우선&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;ASF 30개+ 기업 공동 거버넌스, 클라우드 3사 네이티브&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;sub-second freshness + Flink Lookup Join + CDC 핫 경로&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Apache Fluss&lt;/td&gt;
&lt;td&gt;PK Table 500K QPS 포인트 룩업, Flink 차원 테이블 외부화, A/B 카운터 &amp;mdash; Paimon/Iceberg Tiering으로 배치 쿼리까지 자동 연동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실시간 수집 + 배치 멀티엔진 동시 필요&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Paimon + Iceberg 혼합&lt;/td&gt;
&lt;td&gt;Flink 실시간 수집은 Paimon, 멀티엔진 분석은 Iceberg &amp;mdash; Iceberg 호환 스냅샷으로 무복사 브리징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CDC 수집 + 장기 대용량 배치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hudi(Bronze) + Iceberg(Silver/Gold)&lt;/td&gt;
&lt;td&gt;CDC/Upsert는 Hudi MOR, 정제&amp;middot;집계는 Iceberg &amp;mdash; XTable로 메타데이터 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Databricks Spark + 멀티클라우드 분석&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta + Iceberg (UniForm)&lt;/td&gt;
&lt;td&gt;Delta 단일 쓰기, UniForm으로 Iceberg 메타데이터 자동 생성 &amp;mdash; Snowflake/Athena 직접 읽기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Flink 실시간 스트리밍 + 장기 마이크로배치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Fluss + Paimon + Iceberg&lt;/td&gt;
&lt;td&gt;핫(Fluss) &amp;rarr; 웜(Paimon) &amp;rarr; 콜드(Iceberg) 3계층 Streamhouse &amp;mdash; Flink SQL Union Read 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://iceberg.apache.org/spec/&quot;&gt;Apache Iceberg 공식 스펙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/delta-io/delta/blob/master/PROTOCOL.md&quot;&gt;Delta Lake Protocol Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.apache.org/docs/&quot;&gt;Apache Hudi 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://paimon.apache.org/docs/master/&quot;&gt;Apache Paimon 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ducklake.select/2026/04/13/ducklake-10/&quot;&gt;DuckLake v1.0 발표 (2026.04)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://duckdb.org/2025/05/27/ducklake&quot;&gt;DuckLake: SQL as a Lakehouse Format &amp;mdash; DuckDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xtable.apache.org/&quot;&gt;Apache XTable (Incubating)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/big-data/unlock-the-power-of-apache-iceberg-v3-deletion-vectors-on-amazon-emr/&quot;&gt;Iceberg v3 Deletion Vectors on Amazon EMR &amp;mdash; AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hudi.apache.org/blog/2025/01/28/concurrency-control/&quot;&gt;Concurrency Control in Open Data Lakehouse &amp;mdash; Apache Hudi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lakefs.io/blog/hudi-iceberg-and-delta-lake-data-lake-table-formats-compared/&quot;&gt;Hudi vs Iceberg vs Delta Lake 비교 &amp;mdash; lakeFS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://atlan.com/know/iceberg/apache-paimon-vs-iceberg/&quot;&gt;Apache Paimon vs Iceberg &amp;mdash; Atlan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gravitino.apache.org/blog/2025-summary/&quot;&gt;Apache Gravitino 2025 Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://paimon.apache.org/docs/1.1/migration/iceberg-compatibility/&quot;&gt;Paimon Iceberg 호환성 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://paimon.apache.org/docs/master/iceberg/rest-catalog/&quot;&gt;Paimon REST Catalog 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://streamnative.io/data-streaming-summit/recordings/dss-virtual-2025-building-a-modern-streaming-data-pipeline-with-apache-flink-iceberg-and-paimon&quot;&gt;Building a Modern Streaming Data Pipeline with Apache Flink, Iceberg and Paimon &amp;mdash; StreamNative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ververica.com/blog/from-kappa-architecture-to-streamhouse-making-lakehouses-real-time&quot;&gt;From Kappa Architecture to Streamhouse &amp;mdash; Ververica&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.onehouse.ai/blog/using-apache-hudi-data-with-apache-iceberg-and-delta-lake&quot;&gt;Using Apache Hudi Data with Apache Iceberg and Delta Lake &amp;mdash; Onehouse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.capitalone.com/tech/cloud/lakehouse-format-convergence-delta-lake-iceberg/&quot;&gt;Lakehouse Convergence: Delta Lake &amp;amp; Iceberg &amp;mdash; Capital One Tech&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sanj.dev/post/ducklake-iceberg-modern-lakehouse-architecture-2025&quot;&gt;DuckLake &amp;amp; Iceberg: Modern Lakehouse Architecture 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.dremio.com/blog/apache-xtable-converting-between-apache-iceberg-delta-lake-and-apache-hudi/&quot;&gt;Apache XTable: Converting Between Iceberg, Delta Lake, and Hudi &amp;mdash; Dremio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://netflixtechblog.com/incremental-processing-using-netflix-maestro-and-apache-flink-iceberg-b8ba072ddeeb&quot;&gt;Incremental Processing using Netflix Maestro and Apache Iceberg &amp;mdash; Netflix TechBlog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fluss.apache.org/&quot;&gt;Apache Fluss 공식 사이트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fluss.apache.org/docs/streaming-lakehouse/overview/&quot;&gt;Apache Fluss Streaming Lakehouse 개요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fluss.apache.org/blog/releases/0.9/&quot;&gt;Apache Fluss v0.9 릴리스 노트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.alibabacloud.com/blog/apache-fluss-vs--apache-paimon-two-engines-for-the-real-time-lakehouse_602687&quot;&gt;Apache Fluss vs Apache Paimon &amp;mdash; Alibaba Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jack-vanlightly.com/blog/2025/9/2/understanding-apache-fluss&quot;&gt;Understanding Apache Fluss &amp;mdash; Jack Vanlightly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fluss.apache.org/blog/taobao-practice/&quot;&gt;Taobao Practice: Apache Fluss 사례&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부/데이터</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/707</guid>
      <comments>https://brownbears.tistory.com/707#entry707comment</comments>
      <pubDate>Mon, 27 Apr 2026 21:38:05 +0900</pubDate>
    </item>
    <item>
      <title>[Spark] 메모리 관리</title>
      <link>https://brownbears.tistory.com/749</link>
      <description>&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Spark의 메모리 관리는 &lt;b&gt;UnifiedMemoryManager&lt;/b&gt;를 중심으로 Execution, Storage, User 영역이 동적으로 협력하는 구조입니다. 올바른 설정과 전략으로 OOM 에러를 방지하고 성능을 최대화할 수 있습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. Unified Memory Manager 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark 1.6부터 도입된 &lt;b&gt;UnifiedMemoryManager&lt;/b&gt;는 Executor JVM 힙을 세 가지 영역으로 구분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모리 영역 구조&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────┐
│                  Executor JVM Heap (예: 20GB)             │
├──────────────────────────────────────────────────────────┤
│  Reserved Memory       │  300MB (고정, 변경 불가)          │
├──────────────────────────────────────────────────────────┤
│  User Memory           │  Usable &amp;times; (1 - 0.6) = 40%       │
│  (사용자 코드, UDF 등)   │                                  │
├──────────────────────────────┬───────────────────────────┤
│  Storage Memory  (30%)       │  Spark Memory             │
│  캐시, 브로드캐스트 변수       │  Usable &amp;times; 0.6 = 60%       │
├──────────────────────────────┤                           │
│  Execution Memory (30%)      │                           │
│  셔플, 조인, 정렬, 집계        │                           │
└──────────────────────────────┴───────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동적 메모리 차용 (Dynamic Borrowing)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;1774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGQxgA/dJMcaa6aYSP/CYeksAPceNhW8vsxiXqrNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGQxgA/dJMcaa6aYSP/CYeksAPceNhW8vsxiXqrNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGQxgA/dJMcaa6aYSP/CYeksAPceNhW8vsxiXqrNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGQxgA%2FdJMcaa6aYSP%2FCYeksAPceNhW8vsxiXqrNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1976&quot; height=&quot;1774&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;1774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;두 영역은 &lt;b&gt;유휴 공간을 서로 빌려 쓸 수 있습니다.&lt;/b&gt; Execution이 Storage를 강제 퇴출할 수 있으며, Storage는 &lt;code&gt;storageFraction&lt;/code&gt; 이하로는 퇴출당하지 않습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Execution &amp;rarr; Storage 강제 퇴출 가능&lt;/b&gt;: Execution 메모리가 부족하면 Storage 캐시 블록을 내보냄&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Storage &amp;rarr; Execution 유휴 공간 활용 가능&lt;/b&gt;: 단, 나중에 Execution이 요청하면 즉시 반환해야 함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spark.memory.storageFraction&lt;/code&gt;은 Storage의 &lt;b&gt;최소 보장선&lt;/b&gt;으로 작용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모리 계산 공식&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# 실제 예시 (executor.memory = 20GB, 기본값 기준)
executor_memory    = 20 * 1024  # MB
reserved_memory    = 300        # MB (고정)
usable_memory      = executor_memory - reserved_memory  # 19,900MB

user_memory        = usable_memory * (1 - 0.6)   # 7,960MB (~40%)
spark_memory       = usable_memory * 0.6          # 11,940MB (~60%)

storage_memory     = spark_memory * 0.5           # 5,970MB
execution_memory   = spark_memory * 0.5           # 5,970MB&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 핵심 설정 파라미터&lt;/h1&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;파라미터&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;기본값&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.executor.memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1g&lt;/td&gt;
&lt;td&gt;Executor당 JVM 힙 메모리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.executor.memoryOverhead&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max(10%, 384MB)&lt;/td&gt;
&lt;td&gt;JVM 외부 오버헤드 (Metaspace, 네이티브 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.driver.memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1g&lt;/td&gt;
&lt;td&gt;Driver JVM 힙 메모리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.driver.maxResultSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1g&lt;/td&gt;
&lt;td&gt;collect() 결과 최대 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.memory.fraction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.6&lt;/td&gt;
&lt;td&gt;힙 중 Spark가 관리하는 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.memory.storageFraction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;Spark Memory 중 Storage 최소 보장 비율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.sql.shuffle.partitions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;셔플 파티션 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.memory.offHeap.enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Off-Heap 메모리 활성화 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.memory.offHeap.size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Off-Heap 크기 (bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정 예시 (PySpark)&lt;/h2&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName(&quot;MemoryTuning&quot;) \
    .config(&quot;spark.executor.memory&quot;, &quot;8g&quot;) \
    .config(&quot;spark.executor.memoryOverhead&quot;, &quot;2g&quot;) \
    .config(&quot;spark.driver.memory&quot;, &quot;4g&quot;) \
    .config(&quot;spark.driver.maxResultSize&quot;, &quot;2g&quot;) \
    .config(&quot;spark.memory.fraction&quot;, &quot;0.6&quot;) \
    .config(&quot;spark.memory.storageFraction&quot;, &quot;0.5&quot;) \
    .config(&quot;spark.sql.shuffle.partitions&quot;, &quot;400&quot;) \
    .getOrCreate()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정 예시 (Scala)&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession

val conf = new SparkConf()
  .setAppName(&quot;MemoryTuning&quot;)
  .set(&quot;spark.executor.memory&quot;, &quot;8g&quot;)
  .set(&quot;spark.executor.memoryOverhead&quot;, &quot;2g&quot;)
  .set(&quot;spark.driver.memory&quot;, &quot;4g&quot;)
  .set(&quot;spark.memory.fraction&quot;, &quot;0.6&quot;)
  .set(&quot;spark.memory.storageFraction&quot;, &quot;0.5&quot;)
  .set(&quot;spark.sql.shuffle.partitions&quot;, &quot;400&quot;)

val spark = SparkSession.builder().config(conf).getOrCreate()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;spark-submit 명령어 예시&lt;/h2&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;spark-submit \
  --class com.example.MyJob \
  --master yarn \
  --deploy-mode cluster \
  --executor-memory 8g \
  --driver-memory 4g \
  --conf &quot;spark.executor.memoryOverhead=2g&quot; \
  --conf &quot;spark.memory.fraction=0.6&quot; \
  --conf &quot;spark.sql.shuffle.partitions=400&quot; \
  myapp.jar&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 캐시와 퍼시스트 전략&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;StorageLevel 종류 비교&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;StorageLevel&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;메모리&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;디스크&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;직렬화&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY_ONLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;RDD 기본값. 메모리 부족 시 재계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY_AND_DISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;DataFrame 기본값. 메모리 초과 시 디스크로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY_ONLY_SER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O (직렬화)&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;메모리 절약, CPU 오버헤드 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY_AND_DISK_SER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O (직렬화)&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;직렬화 + 디스크 조합, 안전한 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DISK_ONLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;메모리 매우 부족할 때 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MEMORY_ONLY_2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;2 복제본 유지, 내결함성 필요 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OFF_HEAP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Off-heap&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;JVM GC 부하 없음, offHeap 설정 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;StorageLevel 선택 가이드&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvp2ln/dJMcaiQCMIy/tXUpRoB01ylZKjhzCy7L30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvp2ln/dJMcaiQCMIy/tXUpRoB01ylZKjhzCy7L30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvp2ln/dJMcaiQCMIy/tXUpRoB01ylZKjhzCy7L30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvp2ln%2FdJMcaiQCMIy%2FtXUpRoB01ylZKjhzCy7L30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;1832&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;1832&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 예시 (PySpark)&lt;/h2&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;from pyspark import StorageLevel
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName(&quot;CacheDemo&quot;).getOrCreate()
df = spark.range(10_000_000)

# cache() &amp;mdash; DataFrame 기본: MEMORY_AND_DISK
df.cache()

# 명시적 StorageLevel 지정
df.persist(StorageLevel.MEMORY_ONLY)          # 메모리만, 부족 시 재계산
df.persist(StorageLevel.MEMORY_AND_DISK)      # 메모리 후 디스크 스필
df.persist(StorageLevel.MEMORY_ONLY_SER)      # 직렬화로 메모리 절약
df.persist(StorageLevel.MEMORY_AND_DISK_SER)  # 직렬화 + 디스크 조합
df.persist(StorageLevel.DISK_ONLY)            # 디스크만
df.persist(StorageLevel.OFF_HEAP)             # Off-Heap (설정 필요)

# 캐시 해제 (중요: 안 하면 메모리 누수)
df.unpersist()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 예시 (Scala)&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;import org.apache.spark.storage.StorageLevel
import org.apache.spark.sql.SparkSession

val spark = SparkSession.builder().appName(&quot;CacheDemo&quot;).getOrCreate()
val df = spark.range(10000000L)

df.cache()
df.persist(StorageLevel.MEMORY_ONLY)
df.persist(StorageLevel.MEMORY_AND_DISK)
df.persist(StorageLevel.MEMORY_ONLY_SER)
df.persist(StorageLevel.OFF_HEAP)

df.unpersist()
spark.stop()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Off-Heap 메모리&lt;/h1&gt;
&lt;aside&gt;⚡
&lt;p data-ke-size=&quot;size16&quot;&gt;Off-Heap 메모리는 JVM 힙 외부에 할당되어 &lt;b&gt;GC 대상에서 제외&lt;/b&gt;됩니다. 대용량 데이터 캐시나 긴 GC pause가 문제일 때 효과적입니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 메모리 구성&lt;/h2&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Total Container Memory
= spark.executor.memory         (JVM 힙)
+ spark.executor.memoryOverhead (JVM 외부 오버헤드)
+ spark.memory.offHeap.size     (Spark 관리 Off-Heap)
+ pyspark.executor.memory       (Python 프로세스, PySpark만)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정 및 사용 예시&lt;/h2&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession
from pyspark import StorageLevel

spark = SparkSession.builder \
    .appName(&quot;OffHeapDemo&quot;) \
    .config(&quot;spark.executor.memory&quot;, &quot;4g&quot;) \
    .config(&quot;spark.memory.offHeap.enabled&quot;, &quot;true&quot;) \
    .config(&quot;spark.memory.offHeap.size&quot;, &quot;4294967296&quot;)  # 4GB in bytes
    .getOrCreate()

df = spark.range(5_000_000)

# OFF_HEAP StorageLevel로 캐시
df.persist(StorageLevel.OFF_HEAP)
df.count()  # 액션 실행으로 캐시 활성화

print(df.count())  # 캐시에서 읽기
df.unpersist()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Off-Heap 권장 상황&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매우 큰 데이터셋을 캐시할 때 GC 부하 감소가 필요할 경우&lt;/li&gt;
&lt;li&gt;긴 GC pause로 성능이 저하될 때&lt;/li&gt;
&lt;li&gt;Tungsten 기반 연산 최적화가 필요할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. GC 튜닝 (G1GC + Kryo 직렬화)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kryo 직렬화&lt;/h2&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Kryo는 Java 기본 직렬화보다 &lt;b&gt;10배 빠르고&lt;/b&gt; 메모리 효율이 높습니다. &lt;b&gt;Scala/Java&lt;/b&gt;에서는 항상 사용을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PySpark에서는 효과가 제한적&lt;/b&gt;입니다 &amp;mdash; Python UDF/RDD는 pickle을 사용하며, Kryo는 JVM 내부 셔플 등 일부에만 적용됩니다. PySpark 직렬화 최적화는 아래 노트를 참고하세요.&lt;/p&gt;
&lt;/aside&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;spark = SparkSession.builder \
    .appName(&quot;KryoDemo&quot;) \
    .config(&quot;spark.serializer&quot;, &quot;org.apache.spark.serializer.KryoSerializer&quot;) \
    .config(&quot;spark.kryoserializer.buffer.max&quot;, &quot;512m&quot;) \
    .config(&quot;spark.kryoserializer.buffer&quot;, &quot;64m&quot;) \
    .getOrCreate()&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;val conf = new SparkConf()
  .set(&quot;spark.serializer&quot;, &quot;org.apache.spark.serializer.KryoSerializer&quot;)
  .set(&quot;spark.kryoserializer.buffer.max&quot;, &quot;512m&quot;)
  // 커스텀 클래스 등록으로 직렬화 크기 최소화
  .registerKryoClasses(Array(
    classOf[MyDataClass],
    classOf[AnotherClass]
  ))&lt;/code&gt;&lt;/pre&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PySpark에서 직렬화 성능을 높이는 더 효과적인 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Python UDF 대신 &lt;b&gt;Pandas UDF&lt;/b&gt; 사용 (Apache Arrow 기반, 10~100배 빠름)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DataFrame API&lt;/b&gt; 를 최대화하여 Python &amp;harr; JVM 왕복 최소화&lt;/li&gt;
&lt;li&gt;Kryo 설정 자체는 가능하나 Python UDF / RDD 데이터에는 효과 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;G1GC 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark 4.0부터 G1GC가 기본 GC입니다. Spark 3.x에서는 명시적으로 설정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PySpark에서도 JVM Executor에 그대로 적용&lt;/b&gt;됩니다. 단, Python Worker 프로세스는 Python GC로 별도 관리되며 G1GC 설정의 영향을 받지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;spark-submit \
  --conf &quot;spark.executor.extraJavaOptions=\
    -XX:+UseG1GC \
    -XX:G1HeapRegionSize=16m \
    -XX:+UseCompressedOops \
    -XX:InitiatingHeapOccupancyPercent=35 \
    -XX:ConcGCThreads=4 \
    -verbose:gc \
    -XX:+PrintGCDetails&quot; \
  --conf &quot;spark.driver.extraJavaOptions=-XX:+UseG1GC&quot; \
  myapp.jar&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;spark = SparkSession.builder \
    .config(&quot;spark.executor.extraJavaOptions&quot;,
            &quot;-XX:+UseG1GC &quot;
            &quot;-XX:G1HeapRegionSize=16m &quot;
            &quot;-XX:+UseCompressedOops &quot;
            &quot;-XX:InitiatingHeapOccupancyPercent=35&quot;) \
    .config(&quot;spark.serializer&quot;, &quot;org.apache.spark.serializer.KryoSerializer&quot;) \
    .getOrCreate()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GC 진단 가이드&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;증상&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;원인&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full GC 빈번 발생&lt;/td&gt;
&lt;td&gt;Execution 메모리 부족&lt;/td&gt;
&lt;td&gt;&lt;code&gt;spark.memory.fraction&lt;/code&gt; 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minor GC 잦음&lt;/td&gt;
&lt;td&gt;Eden 영역 부족&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-Xmn&lt;/code&gt; 값 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OldGen 거의 꽉 참&lt;/td&gt;
&lt;td&gt;캐시 과다 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;storageFraction&lt;/code&gt; 감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC pause &amp;gt; 1초&lt;/td&gt;
&lt;td&gt;G1GC 설정 미최적화&lt;/td&gt;
&lt;td&gt;&lt;code&gt;G1HeapRegionSize&lt;/code&gt; 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. OOM 해결 방법&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6-1. Executor OOM&lt;/h2&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 원인: 파티션당 데이터 과다, 셔플/조인 시 메모리 부족

# 해결 1: 파티션 수 증가 (파티션당 128~256MB 목표)
df_repartitioned = df.repartition(500)

# 해결 2: 셔플 파티션 수 증가
spark.conf.set(&quot;spark.sql.shuffle.partitions&quot;, &quot;500&quot;)

# 해결 3: Executor 메모리 및 Overhead 증가
spark = SparkSession.builder \
    .config(&quot;spark.executor.memory&quot;, &quot;12g&quot;) \
    .config(&quot;spark.executor.memoryOverhead&quot;, &quot;2g&quot;) \
    .getOrCreate()

# 해결 4: Execution 메모리 비율 증가 (Storage 줄이기)
spark.conf.set(&quot;spark.memory.storageFraction&quot;, &quot;0.3&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6-2. Driver OOM&lt;/h2&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# 잘못된 방법 &amp;mdash; Driver OOM 위험
result = df.collect()  # 전체 데이터를 Driver로 가져옴

# 올바른 방법
result = df.take(100)             # 일부만 가져오기
df.write.parquet(&quot;/output/path&quot;)  # 스토리지에 직접 쓰기

# Driver 메모리 설정
spark = SparkSession.builder \
    .config(&quot;spark.driver.memory&quot;, &quot;8g&quot;) \
    .config(&quot;spark.driver.maxResultSize&quot;, &quot;4g&quot;) \
    .getOrCreate()

# 브로드캐스트 임계값 조정
spark.conf.set(&quot;spark.sql.autoBroadcastJoinThreshold&quot;, &quot;100m&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6-3. Skew (데이터 편향) 처리&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# AQE로 Skew Join 자동 처리 (Spark 3.0+)
spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.skewedPartitionFactor&quot;, &quot;5&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes&quot;, &quot;256m&quot;)

# 수동 Salting 기법 (AQE 미지원 환경)
from pyspark.sql.functions import col, rand, concat, lit, floor

df_salted = df.withColumn(
    &quot;salted_key&quot;,
    concat(col(&quot;key&quot;), lit(&quot;_&quot;), (floor(rand() * 10)).cast(&quot;string&quot;))
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;종합 프로덕션 설정 템플릿&lt;/h2&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;spark = SparkSession.builder \
    .appName(&quot;ProductionJob&quot;) \
    # === 메모리 기본 설정 ===
    .config(&quot;spark.executor.memory&quot;, &quot;8g&quot;) \
    .config(&quot;spark.executor.memoryOverhead&quot;, &quot;2g&quot;) \
    .config(&quot;spark.driver.memory&quot;, &quot;4g&quot;) \
    .config(&quot;spark.driver.maxResultSize&quot;, &quot;2g&quot;) \
    # === 메모리 비율 ===
    .config(&quot;spark.memory.fraction&quot;, &quot;0.6&quot;) \
    .config(&quot;spark.memory.storageFraction&quot;, &quot;0.4&quot;) \
    # === 직렬화 ===
    .config(&quot;spark.serializer&quot;, &quot;org.apache.spark.serializer.KryoSerializer&quot;) \
    .config(&quot;spark.kryoserializer.buffer.max&quot;, &quot;512m&quot;) \
    # === AQE (Spark 3.0+) ===
    .config(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;) \
    .config(&quot;spark.sql.adaptive.coalescePartitions.enabled&quot;, &quot;true&quot;) \
    .config(&quot;spark.sql.adaptive.skewJoin.enabled&quot;, &quot;true&quot;) \
    # === 파티션 ===
    .config(&quot;spark.sql.shuffle.partitions&quot;, &quot;400&quot;) \
    # === GC ===
    .config(&quot;spark.executor.extraJavaOptions&quot;,
            &quot;-XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+UseCompressedOops&quot;) \
    .getOrCreate()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. Spark 3.x 메모리 관리 개선사항&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;버전별 주요 변경사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spark 3.0 &amp;mdash; AQE 도입 및 StaticMemoryManager 제거
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;StaticMemoryManager 완전 제거&lt;/b&gt;: UnifiedMemoryManager만 남음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AQE (Adaptive Query Execution) 정식 도입&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Off-Heap 분리&lt;/b&gt;: &lt;code&gt;memoryOverhead&lt;/code&gt;와 &lt;code&gt;offHeap.size&lt;/code&gt;가 완전히 별개 항목으로 분리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;REST API 메모리 메트릭 강화&lt;/b&gt;: Peak JVM heap, execution/storage 메모리 추적 가능&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-python&quot;&gt;# AQE 활성화 (3.0에서는 기본값 false)
spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.coalescePartitions.enabled&quot;, &quot;true&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Spark 3.2 &amp;mdash; AQE 기본 활성화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;AQE 기본 활성화&lt;/b&gt;: &lt;code&gt;spark.sql.adaptive.enabled=true&lt;/code&gt;가 기본값으로 변경&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AQE 파티션 자동 병합&lt;/b&gt;: 소규모 셔플 파티션을 자동으로 코얼레스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Push-Based Shuffle&lt;/b&gt;: 메모리 효율 향상&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-python&quot;&gt;# 3.2+에서는 별도 설정 없어도 AQE 활성화됨
spark.conf.set(&quot;spark.sql.adaptive.advisoryPartitionSizeInBytes&quot;, &quot;128m&quot;)
spark.conf.set(&quot;spark.sql.adaptive.coalescePartitions.minPartitionNum&quot;, &quot;1&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Spark 3.4 / 3.5 &amp;mdash; Bloom Filter 및 ZSTD 압축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Bloom Filter Join&lt;/b&gt;: 메모리 효율적 조인 전략 도입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ZSTD 압축 기본 지원&lt;/b&gt;: 셔플 데이터 압축으로 메모리/디스크 절약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;State Store 메모리 개선&lt;/b&gt;: 구조적 스트리밍 상태 저장소 최적화&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-python&quot;&gt;spark = SparkSession.builder \
  .config(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;) \
  .config(&quot;spark.sql.optimizer.runtime.bloomFilter.enabled&quot;, &quot;true&quot;) \
  .config(&quot;spark.sql.optimizer.runtime.bloomFilter.creationSideThreshold&quot;, &quot;10m&quot;) \
  .config(&quot;spark.io.compression.codec&quot;, &quot;zstd&quot;) \
  .getOrCreate()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AQE가 OOM을 방지하는 원리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;1782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz35M4/dJMcaaZnPHa/N9OfPXc6a97wKhoQ5RF2DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz35M4/dJMcaaZnPHa/N9OfPXc6a97wKhoQ5RF2DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz35M4/dJMcaaZnPHa/N9OfPXc6a97wKhoQ5RF2DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz35M4%2FdJMcaaZnPHa%2FN9OfPXc6a97wKhoQ5RF2DK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1734&quot; height=&quot;1782&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;1782&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 메모리 관리 체크리스트&lt;/h1&gt;
&lt;aside&gt;✅
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서 Spark 작업을 배포하기 전에 아래 항목들을 점검하면 OOM과 성능 저하를 예방할 수 있습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✨ 설정 단계 체크리스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 메모리 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.executor.memory&lt;/code&gt; 데이터 크기에 맞게 설정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.executor.memoryOverhead&lt;/code&gt; 설정 완료 (executor.memory의 10% 이상 또는 384MB)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.driver.memory&lt;/code&gt; 설정 완료&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.driver.maxResultSize&lt;/code&gt; 제한 설정 (collect() 남용 방지)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.sql.shuffle.partitions&lt;/code&gt; 데이터 크기에 맞게 조정 (파티션당 128~256MB 목표)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;직렬화 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Kryo 직렬화 활성화 (&lt;code&gt;spark.serializer=KryoSerializer&lt;/code&gt;) &amp;mdash; &lt;b&gt;Scala/Java 권장&lt;/b&gt; (PySpark는 JVM 셔플 일부에만 적용됨)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.kryoserializer.buffer.max&lt;/code&gt; 설정 (512m 권장)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Scala 프로젝트라면 코드내 커스텀 클래스 Kryo 등록 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; PySpark라면 Python UDF 대신 &lt;b&gt;Pandas UDF&lt;/b&gt; 사용 여부 검토 (Arrow 기반, 성능 대폭 향상)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GC 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.executor.extraJavaOptions에&lt;/code&gt; G1GC 설정 포함 (&lt;code&gt;-XX:+UseG1GC&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.memory.fraction&lt;/code&gt; 기본값(0.6) 사용 여부 검토&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.memory.storageFraction&lt;/code&gt; 케시 비율 요구사항에 맞게 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Off-Heap (필요 시에만)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.memory.offHeap.enabled=true&lt;/code&gt; 설정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.memory.offHeap.size&lt;/code&gt; 적절히 설정&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 컨테이너 전체 메모리 = executor.memory + memoryOverhead + offHeap.size 카운팅 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  코드 단계 체크리스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐시 관리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 반복 사용되는 DataFrame/RDD에만 캐시 적용&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사용 완료된 캐시는 &lt;code&gt;unpersist()&lt;/code&gt; 명시적으로 해제&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 데이터 크기에 맞는 StorageLevel 선택 (메모리 부족 시 &lt;code&gt;MEMORY_AND_DISK_SER&lt;/code&gt; 고려)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 사용되지 않는 컨럼은 조기에 &lt;code&gt;select()&lt;/code&gt;/&lt;code&gt;drop()&lt;/code&gt;으로 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 처리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;collect()&lt;/code&gt; 호출 없음 (대신 &lt;code&gt;write()&lt;/code&gt; 또는 &lt;code&gt;take()&lt;/code&gt; 사용)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 브로드캐스트 변수 크기 확인 (100MB 이하 권장)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 파티션 크기 적절함 (파티션당 128~256MB 목표)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Skew 위험이 있는 조인에 AQE 또는 Salting 적용 검토&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  운영 모니터링 체크리스트&lt;/h2&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark UI (일반적으로 &lt;code&gt;:4040&lt;/code&gt; 포트)에서 실시간으로 메모리 상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spark UI 확인 항목&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;b&gt;Executors 탭&lt;/b&gt; &amp;rarr; Storage Memory Used 비율 80% 이하 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;b&gt;Stages 탭&lt;/b&gt; &amp;rarr; Spill (Memory) / Spill (Disk) 발생 여부&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;b&gt;Stages 탭&lt;/b&gt; &amp;rarr; GC Time 비율 5% 이하 유지&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;b&gt;Tasks 탭&lt;/b&gt; &amp;rarr; Duration 편차 확인 (Skew 여부)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 지표 임계값&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;지표&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;정상&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;경고&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;위험&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GC Time 비율&lt;/td&gt;
&lt;td&gt;&amp;lt; 5%&lt;/td&gt;
&lt;td&gt;5 ~ 10%&lt;/td&gt;
&lt;td&gt;&amp;gt; 10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Memory 사용률&lt;/td&gt;
&lt;td&gt;&amp;lt; 70%&lt;/td&gt;
&lt;td&gt;70 ~ 85%&lt;/td&gt;
&lt;td&gt;&amp;gt; 85%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spill (Disk)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;가끔 발생&lt;/td&gt;
&lt;td&gt;빈번 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task 최대/평균 Duration 비율&lt;/td&gt;
&lt;td&gt;&amp;lt; 2x&lt;/td&gt;
&lt;td&gt;2 ~ 5x&lt;/td&gt;
&lt;td&gt;&amp;gt; 5x (Skew 의심)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  OOM 발생 시 진단 체크리스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Executor OOM 진단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 에러 메시지에 &lt;code&gt;Java heap space&lt;/code&gt; 포함 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Spark UI에서 가장 크거나 느린 Task 식별&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Spill (Memory/Disk) 발생 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.sql.shuffle.partitions&lt;/code&gt; 증가 (2배씨 조정)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.executor.memory&lt;/code&gt; 증가&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.executor.memoryOverhead&lt;/code&gt; 증가 (네이티브 라이브러리 사용 시)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Driver OOM 진단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 에러 메시지에 &lt;code&gt;Java heap space&lt;/code&gt; 포함 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 코드내 &lt;code&gt;collect()&lt;/code&gt; 호출 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.driver.maxResultSize&lt;/code&gt; 초과 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 브로드캐스트 변수 크기 확인 (100MB 이상 제한 검토)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; &lt;code&gt;spark.driver.memory&lt;/code&gt; 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Skew OOM 진단&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Task Duration 편차가 큰지 Spark UI에서 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 특정 키에 데이터가 편중되는지 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; AQE &lt;code&gt;skewJoin&lt;/code&gt; 활성화 여부 확인&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 필요 시 Salting 기법 적용 검토&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;참고 자료&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/tuning.html&quot;&gt;Apache Spark 공식 튜닝 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/configuration.html&quot;&gt;Apache Spark 설정 레퍼런스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/memory/UnifiedMemoryManager.scala&quot;&gt;UnifiedMemoryManager 소스 코드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/LucaCanali/Miscellaneous/blob/master/Spark_Notes/Spark_Memory_Configuration.md&quot;&gt;Spark Memory Configuration &amp;mdash; LucaCanali/CERN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/749</guid>
      <comments>https://brownbears.tistory.com/749#entry749comment</comments>
      <pubDate>Mon, 27 Apr 2026 21:21:24 +0900</pubDate>
    </item>
    <item>
      <title>LLM, RAG, LangChain, LangGraph, MCP 개념 및 예시</title>
      <link>https://brownbears.tistory.com/715</link>
      <description>&lt;h1&gt;I. LLM (대규모 언어 모델)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 언어 모델(LLM)은 방대한 텍스트로 학습된 AI 시스템으로, 자연어를 이해하고 생성합니다. 핵심 원리는 &lt;b&gt;다음 토큰 예측&lt;/b&gt;이며, 이를 통해 번역&amp;middot;요약&amp;middot;코드 생성 같은 다양한 작업을 수행합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LLM의 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM에는 내재적 한계가 있으며, 이 때문에 RAG&amp;middot;MCP&amp;middot;LangChain 같은 기술이 등장했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;환각(Hallucination)&lt;/b&gt;: 사실이 아닌 내용을 그럴듯하게 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지식 단절(Knowledge Cutoff)&lt;/b&gt;: 마지막 훈련 날짜 이후 정보 없음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도메인 특수성 부족&lt;/b&gt;: 전문 분야 깊이 있는 지식 부족&lt;/li&gt;
&lt;li&gt;&lt;b&gt;높은 컴퓨팅 비용&lt;/b&gt;: 대규모 자원 필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;편향(Bias)&lt;/b&gt;: 훈련 데이터의 편향을 학습&amp;middot;증폭&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;산업별 응용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고객 서비스&lt;/b&gt;: 자동화 챗봇 및 대화형 에이전트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의료&lt;/b&gt;: 환자 보고서 분석 및 관리 업무 간소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;금융&lt;/b&gt;: 사기 탐지, 금융 리스크 평가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;콘텐츠 제작&lt;/b&gt;: 기사, 마케팅 문구 등 텍스트 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 LLM 호출 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install langchain-openai
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;, temperature=0)
response = llm.invoke(&quot;셀프 어텐션 개념을 한 문장으로 설명해줘.&quot;)
print(response.content)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;II. RAG (검색 증강 생성)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 증강 생성(RAG)은 응답 생성 전에 외부 지식 베이스에서 관련 정보를 검색하여 LLM 출력을 향상시키는 아키텍처 패턴입니다. LLM의 환각과 지식 단절 문제를 해결합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAG 파이프라인 구조&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: 데이터 인덱싱 (오프라인)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 로딩&lt;/b&gt;: PDF, 데이터베이스, API 등에서 데이터 수집&lt;/li&gt;
&lt;li&gt;&lt;b&gt;청킹&lt;/b&gt;: 큰 문서를 작은 단위로 분할&lt;/li&gt;
&lt;li&gt;&lt;b&gt;임베딩&lt;/b&gt;: 텍스트를 수치 벡터로 변환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱싱&lt;/b&gt;: 벡터 DB에 저장 (Pinecone, Chroma, FAISS 등)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: 검색 및 생성 (실시간)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;사용자 쿼리&lt;/b&gt; 입력&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쿼리 임베딩&lt;/b&gt;: 쿼리를 벡터로 변환&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색&lt;/b&gt;: 유사한 문서 청크 검색&lt;/li&gt;
&lt;li&gt;&lt;b&gt;증강&lt;/b&gt;: 검색 결과를 프롬프트에 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생성&lt;/b&gt;: LLM이 맥락을 바탕으로 답변 생성&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정확성 향상 및 환각 감소&lt;/li&gt;
&lt;li&gt;실시간 최신 데이터 접근&lt;/li&gt;
&lt;li&gt;미세 조정보다 비용 효율적&lt;/li&gt;
&lt;li&gt;출처 인용 가능 (투명성)&lt;/li&gt;
&lt;li&gt;독점 데이터 프라이버시 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;과제&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 품질에 크게 의존&lt;/li&gt;
&lt;li&gt;청킹 전략이 성능에 큰 영향&lt;/li&gt;
&lt;li&gt;대규모 데이터 인덱싱 비용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAG 파이프라인 vs. RAG 플랫폼&lt;/h2&gt;
&lt;p&gt;기능 RAG 파이프라인 (직접 구축) RAG 플랫폼 (통합 제품)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;통합 노력&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음 &amp;mdash; 구성 요소 직접 통합&lt;/td&gt;
&lt;td&gt;낮음 &amp;mdash; 즉시 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;프로덕션 준비성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;낮음 &amp;mdash; 직접 구축 필요&lt;/td&gt;
&lt;td&gt;높음 &amp;mdash; 엔터프라이즈급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;복잡 &amp;mdash; 수동 관리 필요&lt;/td&gt;
&lt;td&gt;간편 &amp;mdash; 자동 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유연성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;가치 실현 시간&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;길다 (수개월)&lt;/td&gt;
&lt;td&gt;짧다 (즉시)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 RAG 라이브러리&lt;/h2&gt;
&lt;p&gt;라이브러리 특징 최적 사용 사례&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LangChain&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;방대한 통합, LCEL 체인&lt;/td&gt;
&lt;td&gt;다양한 LLM 앱 프로토타이핑&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터-LLM 연결 특화&lt;/td&gt;
&lt;td&gt;대규모 데이터셋 RAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Haystack&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;검색&amp;middot;QA 파이프라인&lt;/td&gt;
&lt;td&gt;프로덕션급 검색 시스템&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAG 구현 예시&lt;/h2&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# pip install langchain langchain-openai faiss-cpu beautifulsoup4
from langchain_community.document_loaders import WebBaseLoader
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
from langchain_openai import ChatOpenAI

loader = WebBaseLoader(&quot;&amp;lt;https://en.wikipedia.org/wiki/Large_language_model&amp;gt;&quot;)
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)

embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)
retriever = vectorstore.as_retriever()

llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;)
prompt = ChatPromptTemplate.from_template(&quot;&quot;&quot;다음 컨텍스트만 사용해서 답해줘:


{context}


질문: {input}&quot;&quot;&quot;)

document_chain = create_stuff_documents_chain(llm, prompt)
retrieval_chain = create_retrieval_chain(retriever, document_chain)

response = retrieval_chain.invoke({&quot;input&quot;: &quot;What is a large language model?&quot;})
print(response[&quot;answer&quot;])
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;III. LangChain&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangChain은 LLM 기반 애플리케이션 개발을 위한 오픈소스 프레임워크로, 다양한 구성 요소를 연결하는 '접착제' 역할을 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 구성 요소&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;모델(Models)&lt;/b&gt;: LLM&amp;middot;채팅 모델&amp;middot;임베딩 모델을 위한 표준 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프롬프트(Prompts)&lt;/b&gt;: 동적 프롬프트 템플릿 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;체인(Chains)&lt;/b&gt;: LLM과 다른 구성 요소를 순차 결합 (LCEL 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색(Retrieval)&lt;/b&gt;: 문서 로더&amp;middot;분할기&amp;middot;벡터 저장소&amp;middot;검색기 (RAG 핵심)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에이전트(Agents)&lt;/b&gt;: LLM을 추론 엔진으로 사용, 도구 선택&amp;middot;실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리(Memory)&lt;/b&gt;: 호출 간 상태 유지, 다중 턴 대화&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;강점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수백 개 서드파티 통합 제공&lt;/li&gt;
&lt;li&gt;고수준 추상화로 빠른 프로토타이핑&lt;/li&gt;
&lt;li&gt;공통 구성 요소 표준 인터페이스&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;약점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상화가 디버깅을 어렵게 할 수 있음&lt;/li&gt;
&lt;li&gt;빠른 변화로 호환성 문제 가능&lt;/li&gt;
&lt;li&gt;복잡한 순환 워크플로우 구현 어려움 &amp;rarr; LangGraph 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RAG 챗봇&lt;/b&gt;: 내부 문서 기반 Q&amp;amp;A&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리 챗봇&lt;/b&gt;: 과거 대화를 기억하는 에이전트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문서 요약&lt;/b&gt;: 맵-리듀스 방식으로 긴 문서 요약&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동화 에이전트&lt;/b&gt;: 웹 검색 등 도구를 활용하는 에이전트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;맵-리듀스 요약 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install langchain langchain-openai
from langchain_openai import ChatOpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document

llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;, temperature=0)

long_text = &quot;&quot;&quot;
Large language models are advanced AI systems that understand and generate natural language.
LLMs are trained on vast amounts of text data.
However, LLMs have limitations such as knowledge cutoff and hallucination.
RAG connects LLMs to external knowledge bases to address these issues.
&quot;&quot;&quot;

docs = [Document(page_content=long_text)]
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
split_docs = text_splitter.split_documents(docs)

chain = load_summarize_chain(llm, chain_type=&quot;map_reduce&quot;)
summary = chain.invoke(split_docs)
print(summary['output_text'])
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;IV. LangGraph&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LangGraph는 LangChain의 확장으로, 상태를 가진 다중 에이전트 애플리케이션을 그래프 형태로 구성합니다. 루프&amp;middot;분기&amp;middot;조건 로직이 필요한 복잡한 워크플로우에 적합합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 구성 요소&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상태(State)&lt;/b&gt;: 그래프 전체에서 공유되는 데이터 구조 (TypedDict)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;노드(Nodes)&lt;/b&gt;: 작업 단위인 파이썬 함수 (LLM 호출, 도구 실행 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엣지(Edges)&lt;/b&gt;: 노드 간 흐름 제어. 조건부 엣지로 동적 라우팅 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LangChain vs. LangGraph&lt;/h2&gt;
&lt;p&gt;기준 LangChain LangGraph&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;워크플로우&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;선형, 순차적 (DAG)&lt;/td&gt;
&lt;td&gt;순환 그래프, 루프, 복잡한 분기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상태 관리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;암시적 (메모리 객체)&lt;/td&gt;
&lt;td&gt;명시적 중앙 집중 상태 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Human-in-the-Loop&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;구현 복잡&lt;/td&gt;
&lt;td&gt;중단점 기능으로 기본 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;다중 에이전트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;기본 설계 목적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;단순 작업&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;간단하고 직관적&lt;/td&gt;
&lt;td&gt;설정 많이 필요, 과도할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 사용할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangChain 사용&lt;/b&gt;: 단순 순차 워크플로우, 기본 RAG 파이프라인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LangGraph 사용&lt;/b&gt;: 루프&amp;middot;자기 교정이 필요한 경우, 명시적 상태 관리, 다중 에이전트 협업, Human-in-the-Loop&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LangGraph 상태 기반 에이전트 예시&lt;/h2&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# pip install langgraph langchain-openai pydantic
from typing import TypedDict, List, Optional
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langgraph.graph import StateGraph, END

llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;, temperature=0)

class AgentState(TypedDict):
    task: str
    plan: Optional[List[str]]
    executed_steps: Optional[List[str]]
    response: str

class Plan(BaseModel):
    steps: List[str] = Field(description=&quot;실행할 단계 목록&quot;)

planner_prompt = ChatPromptTemplate.from_template(&quot;다음 작업을 위한 단계별 계획을 세워줘: {task}&quot;)
planner = (planner_prompt | llm).with_structured_output(Plan)

def plan_step(state: AgentState) -&amp;gt; dict:
    result = planner.invoke({&quot;task&quot;: state['task']})
    return {&quot;plan&quot;: result.steps, &quot;executed_steps&quot;: []}

executor_prompt = ChatPromptTemplate.from_template(
    &quot;'{step}' 단계를 실행해줘. 이전 단계: {executed_steps}. 작업: {task}&quot;
)
executor = executor_prompt | llm

def execute_step(state: AgentState) -&amp;gt; dict:
    plan = state['plan']
    executed = state['executed_steps']
    current_step = plan[len(executed)]
    result = executor.invoke({
        &quot;step&quot;: current_step,
        &quot;executed_steps&quot;: &quot;, &quot;.join(executed) if executed else &quot;없음&quot;,
        &quot;task&quot;: state['task']
    })
    executed.append(f&quot;{current_step}: {result.content}&quot;)
    return {&quot;executed_steps&quot;: executed}

def final_response(state: AgentState) -&amp;gt; dict:
    return {&quot;response&quot;: &quot;\\n&quot;.join(state['executed_steps'])}

def should_continue(state: AgentState) -&amp;gt; str:
    return &quot;continue&quot; if len(state['executed_steps']) &amp;lt; len(state['plan']) else &quot;end&quot;

workflow = StateGraph(AgentState)
workflow.add_node(&quot;planner&quot;, plan_step)
workflow.add_node(&quot;executor&quot;, execute_step)
workflow.add_node(&quot;final_responder&quot;, final_response)

workflow.set_entry_point(&quot;planner&quot;)
workflow.add_edge(&quot;planner&quot;, &quot;executor&quot;)
workflow.add_conditional_edges(&quot;executor&quot;, should_continue, {
    &quot;continue&quot;: &quot;executor&quot;,
    &quot;end&quot;: &quot;final_responder&quot;
})
workflow.add_edge(&quot;final_responder&quot;, END)

app = workflow.compile()
result = app.invoke({&quot;task&quot;: &quot;RAG의 장점에 대한 블로그 포스트 작성&quot;})
print(result['response'])
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;V. MCP (모델 컨텍스트 프로토콜)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP(Model Context Protocol)는 AI 시스템이 외부 도구 및 데이터 소스와 통신하는 방식을 표준화한 오픈 표준입니다. &lt;b&gt;&quot;AI를 위한 USB-C&quot;&lt;/b&gt; &amp;mdash; 어떤 AI 모델도 MCP를 구현한 도구에 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic이 만들었으며 OpenAI, Google DeepMind, Microsoft 등 주요 AI 기업이 채택했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MCP 서버&lt;/b&gt;: 도구와 데이터를 노출하는 애플리케이션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MCP 클라이언트&lt;/b&gt;: AI 호스트에 내장되어 서버에 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 사용 사례&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;엔터프라이즈 어시스턴트&lt;/b&gt;: 챗봇을 내부 문서&amp;middot;CRM&amp;middot;지식 베이스에 연결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다중 도구 에이전트&lt;/b&gt;: 여러 도구 조율 (Google Drive 조회 후 Slack 전송 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발 도구&lt;/b&gt;: AI를 IDE 및 개발 환경에 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fastMCP 사용 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install fastmcp
import asyncio
from fastmcp import Client

async def main():
    client = Client(&quot;mcp://calendar.example.com&quot;)

    async with client:
        tools = await client.list_tools()
        print(f&quot;사용 가능한 도구: {[tool.name for tool in tools]}&quot;)

        response = await client.call_tool(
            &quot;create_event&quot;,
            {&quot;title&quot;: &quot;팀 미팅&quot;, &quot;date&quot;: &quot;2025-12-01&quot;}
        )
        print(f&quot;서버 응답: {response}&quot;)

asyncio.run(main())
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;aside&amp;gt; ⚠️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보안 주의&lt;/b&gt;: MCP는 도구 및 데이터 접근을 허용하므로 프롬프트 주입, 과도한 권한 부여, 악의적 도구 등 보안 위험에 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;h1&gt;VI. 기술 스택 통합&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 기술은 경쟁 관계가 아닌 상호 보완적인 계층입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;LLM&lt;/b&gt;: 생성 능력의 기초&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RAG&lt;/b&gt;: 외부 지식 베이스 연결, 환각 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangChain&lt;/b&gt;: RAG 파이프라인 구성 및 오케스트레이션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangGraph&lt;/b&gt;: 루프&amp;middot;조건&amp;middot;다중 에이전트 워크플로우 관리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MCP&lt;/b&gt;: 외부 도구&amp;middot;API와 표준화된 통신&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;도구 선택 가이드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;RAG&lt;/b&gt;: 정확성이 필요한 모든 앱의 출발점&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangChain&lt;/b&gt;: 단순 순차 체인, 빠른 프로토타이핑&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangGraph&lt;/b&gt;: 루프&amp;middot;조건&amp;middot;다중 에이전트 필요 시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MCP&lt;/b&gt;: 외부 서비스 연결 표준화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;미래 방향&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패러다임은 단일 모델에서 &lt;b&gt;모듈식&amp;middot;전문화&amp;middot;오케스트레이션된 시스템&lt;/b&gt;으로 이동 중입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;플로우 엔지니어링&quot;: 프롬프트 최적화보다 에이전트 행동 설계에 집중&lt;/li&gt;
&lt;li&gt;MCP 표준화로 도구 생태계 통합 가속&lt;/li&gt;
&lt;li&gt;Multi-Agent 시스템이 복잡한 실무 문제 해결의 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;VII. CrewAI&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CrewAI는 &lt;b&gt;역할 기반 AI 에이전트들이 팀을 이루어 협업&lt;/b&gt;하는 Python 프레임워크입니다. 각 에이전트가 직무(role)&amp;middot;목표(goal)&amp;middot;배경(backstory)&amp;middot;도구(tools)를 가지고 일하며, Crew가 Process에 따라 Task를 처리합니다. 2026년 기준 안정 버전은 v1.14.3이며, Crew(자율 협업)와 Flow(이벤트 기반 결정론 워크플로우) 두 축으로 구성됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LangChain / LangGraph와의 차이&lt;/h2&gt;
&lt;p&gt;항목 LangChain LangGraph CrewAI&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;핵심 추상&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LLM/도구/체인 빌딩 블록&lt;/td&gt;
&lt;td&gt;상태 머신 그래프&lt;/td&gt;
&lt;td&gt;역할 기반 에이전트 팀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드량&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;많음 (명시적 제어)&lt;/td&gt;
&lt;td&gt;적음 (~20줄로 동작)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;강점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;도구 생태계, RAG&lt;/td&gt;
&lt;td&gt;결정론, 체크포인트, HITL&lt;/td&gt;
&lt;td&gt;빠른 프로토타이핑, 직관적 역할 분담&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;약점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;멀티에이전트 추상 약함&lt;/td&gt;
&lt;td&gt;보일러플레이트 많음&lt;/td&gt;
&lt;td&gt;분기/루프 복잡한 워크플로에 부적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LangChain 도구 호환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;완전 호환&lt;/td&gt;
&lt;td&gt;완전 호환 (LiteLLM 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 구성 요소&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Agent&lt;/b&gt;: role + goal + backstory + tools + llm + allow_delegation&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Task&lt;/b&gt;: description + expected_output + agent + context (이전 태스크 출력 주입)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Crew&lt;/b&gt;: 에이전트&amp;middot;태스크 컨테이너. memory=True, cache=True, verbose=True 설정 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Process&lt;/b&gt;: sequential (순서대로) / hierarchical (매니저 에이전트가 동적 위임)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뉴스 리서처 + 작가 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install &quot;crewai&amp;gt;=1.14&quot; &quot;crewai-tools&quot;
from dotenv import load_dotenv
from crewai import Agent, Task, Crew, Process, LLM
from crewai_tools import SerperDevTool

load_dotenv()  # OPENAI_API_KEY, SERPER_API_KEY

llm = LLM(model=&quot;openai/gpt-4o-mini&quot;, temperature=0.2)
search_tool = SerperDevTool(n_results=5, search_type=&quot;news&quot;)

researcher = Agent(
    role=&quot;시니어 AI 뉴스 리서처&quot;,
    goal=&quot;{topic}에 대한 최근 7일 내 뉴스 5건을 찾아 요약한다.&quot;,
    backstory=&quot;10년 경력의 IT 저널리스트. 출처 URL과 발행일을 항상 포함한다.&quot;,
    tools=[search_tool],
    llm=llm,
    verbose=True,
)

writer = Agent(
    role=&quot;테크 콘텐츠 작가&quot;,
    goal=&quot;리서치 결과를 한국어 뉴스레터로 작성한다.&quot;,
    backstory=&quot;비전공 독자도 이해할 수 있는 기술 트렌드 작가.&quot;,
    llm=llm,
)

research_task = Task(
    description=&quot;{topic} 관련 최신 뉴스 5건 수집. 제목/날짜/출처/요점 3개/URL 포함.&quot;,
    expected_output=&quot;5개 항목의 마크다운 리스트&quot;,
    agent=researcher,
)

writing_task = Task(
    description=&quot;리서치 결과로 한국어 뉴스레터를 작성한다. 각 기사는 H3 + 4~5문장 + 원문 링크.&quot;,
    expected_output=&quot;발행 가능한 한국어 마크다운 뉴스레터&quot;,
    agent=writer,
    context=[research_task],
    output_file=&quot;newsletter.md&quot;,
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, writing_task],
    process=Process.sequential,
    memory=True,
    verbose=True,
)

result = crew.kickoff(inputs={&quot;topic&quot;: &quot;온디바이스 LLM 최신 동향&quot;})
print(result.raw)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MCP 통합 예시&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install &quot;crewai-tools[mcp]&quot;
from mcp import StdioServerParameters
from crewai_tools import MCPServerAdapter
from crewai import Agent, Task, Crew

params = StdioServerParameters(command=&quot;uvx&quot;, args=[&quot;pubmedmcp@0.1.3&quot;])
with MCPServerAdapter(params) as tools:
    agent = Agent(role=&quot;Medical Researcher&quot;, goal=&quot;PubMed 검색&quot;, tools=tools)
    task = Task(description=&quot;CRISPR 최신 논문 5건 요약&quot;, agent=agent,
                expected_output=&quot;요약 리스트&quot;)
    Crew(agents=[agent], tasks=[task]).kickoff()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 사용할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CrewAI&lt;/b&gt;: 명확한 역할 분담, 빠른 프로토타이핑, 콘텐츠/리서치 파이프라인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LangGraph&lt;/b&gt;: 분기&amp;middot;루프&amp;middot;Human-in-the-Loop&amp;middot;체크포인트가 필요한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조합&lt;/b&gt;: LangGraph(외곽 컨트롤러) + CrewAI(내부 창의 협업 노드) + MCP(도구 표준)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;VIII. Claude Code 개념&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Claude Code vs Claude Desktop&lt;/h2&gt;
&lt;p&gt;항목 Claude Code (CLI) Claude Desktop (GUI)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;npm install -g @anthropic-ai/claude-code&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://claude.ai/download&quot;&gt;claude.ai/download&lt;/a&gt; 에서 .dmg/.exe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주 사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;코드베이스 작업, git 자동화, 멀티파일 리팩터링&lt;/td&gt;
&lt;td&gt;일반 대화, 문서 작업, Artifacts 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;에이전트/서브에이전트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Task 도구로 서브에이전트 스폰, 병렬 실행&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스킬 (Skill)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~/.claude/skills/ 슬래시 커맨드&lt;/td&gt;
&lt;td&gt;Custom Skills (&lt;a href=&quot;http://Claude.ai&quot;&gt;Claude.ai&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hooks&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;settings.json 라이프사이클 이벤트&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Artifacts&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;없음 (파일 직접 편집)&lt;/td&gt;
&lt;td&gt;우측 패널 인터랙티브 콘텐츠&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;MCP 추가 방법&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;claude mcp add &amp;lt;name&amp;gt;&lt;/td&gt;
&lt;td&gt;.mcpb 더블클릭 또는 config.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;커넥터&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;Notion, GDrive, GitHub, Slack 등 50+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에이전트 &amp;amp; 서브에이전트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 에이전트는 사용자와 직접 대화하는 세션입니다. &lt;b&gt;서브에이전트&lt;/b&gt;는 Task 도구로 스폰되는 격리된 워커로, 독립적인 컨텍스트&amp;middot;권한&amp;middot;모델을 보유합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;# 단일 메시지에서 독립 태스크를 병렬로 실행 (최대 7개 동시)
Task({ subagent_type: &quot;general-purpose&quot;, model: &quot;haiku&quot;,  prompt: &quot;파일 검색&quot; })
Task({ subagent_type: &quot;code-reviewer&quot;,  model: &quot;sonnet&quot;, prompt: &quot;코드 리뷰&quot; })
Task({ subagent_type: &quot;executor&quot;,       model: &quot;opus&quot;,   prompt: &quot;복잡한 구현&quot; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델 라우팅&lt;/b&gt;: haiku (빠른 조회) / sonnet (표준, 기본) / opus (복잡한 분석)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브에이전트 정의 파일: .claude/agents/&amp;lt;name&amp;gt;.md (프로젝트) 또는 ~/.claude/agents/&amp;lt;name&amp;gt;.md (글로벌)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스킬 (Skill)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/skill-name 슬래시 커맨드로 호출하거나 description 매칭으로 자동 트리거됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일 위치&lt;/b&gt;: ~/.claude/skills/&amp;lt;name&amp;gt;/SKILL.md (글로벌) / .claude/skills/&amp;lt;name&amp;gt;/SKILL.md (프로젝트)&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;---
name: my-skill
description: When user asks for X, do Y...
---

# Skill 내용
구체적인 instructions...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 name + description만 로드하고 (&amp;asymp;100 토큰), 매칭되면 full body를 로드합니다 (&amp;le;5K 토큰).&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;툴 (Tool)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내장 툴&lt;/b&gt;: Read, Write, Edit, Bash, Task, Grep, Glob, WebFetch, WebSearch&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Deferred 툴&lt;/b&gt;: 모든 툴 스키마를 한 번에 로드하지 않고 ToolSearch로 필요할 때 로드 &amp;rarr; 컨텍스트 절약&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# MCP 외부 툴 추가
claude mcp add github --scope user
claude mcp add playwright npx @playwright/mcp@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hooks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 이벤트&lt;/b&gt;: PreToolUse / PostToolUse / UserPromptSubmit / Stop / SessionStart&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PostToolUse&quot;: [{
      &quot;matcher&quot;: &quot;Write|Edit|MultiEdit&quot;,
      &quot;hooks&quot;: [{
        &quot;type&quot;: &quot;command&quot;,
        &quot;command&quot;: &quot;npx prettier --write \\&quot;$CLAUDE_TOOL_INPUT_FILE_PATH\\&quot;&quot;
      }]
    }],
    &quot;Stop&quot;: [{
      &quot;hooks&quot;: [{ &quot;type&quot;: &quot;command&quot;, &quot;command&quot;: &quot;scripts/notify-done.sh&quot; }]
    }]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exit code: 0 (통과) / 2 (PreToolUse에서 툴 차단, 또는 Stop hook에서 계속 강제)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모리 (Memory)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://CLAUDE.md&quot;&gt;**CLAUDE.md&lt;/a&gt; 계층** (우선순위 높은 순):&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Managed Policy (관리자 배포)&lt;/li&gt;
&lt;li&gt;Project Memory (./CLAUDE.md)&lt;/li&gt;
&lt;li&gt;Project Rules (.claude/rules/*.md 자동 로드)&lt;/li&gt;
&lt;li&gt;User Memory (~/.claude/CLAUDE.md)&lt;/li&gt;
&lt;li&gt;Local Project (./CLAUDE.local.md)&lt;/li&gt;
&lt;li&gt;Auto Memory&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좋은 &lt;a href=&quot;http://CLAUDE.md&quot;&gt;CLAUDE.md&lt;/a&gt; 예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# 프로젝트명

## 기술 스택
- Python 3.12, FastAPI, Postgres 16

## 빌드/테스트
- 빌드: `make build`
- 테스트: `pytest tests/`

## 컨벤션
- snake_case 함수, PascalCase 클래스
- public API는 type hint 필수

## 금지사항
- main 브랜치 직접 push 금지
- print() 디버깅 금지

@.claude/rules/api-design.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자동 메모리 시스템&lt;/b&gt;: ~/.claude/projects/&amp;lt;encoded-cwd&amp;gt;/memory/MEMORY.md (인덱스)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입: user / feedback / project / reference&lt;/li&gt;
&lt;li&gt;&amp;lt;remember&amp;gt;: 7일 단기 / &amp;lt;remember priority&amp;gt;: 영구 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아티팩트 (Artifact)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://Claude.ai&quot;&gt;Claude.ai&lt;/a&gt; Web / Desktop 전용 기능. 우측 패널에 렌더링되는 독립 콘텐츠입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;생성 조건&lt;/b&gt;: 15줄 이상의 자립적 콘텐츠, 편집/재사용 가능성 높은 콘텐츠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형&lt;/b&gt;: 코드, HTML/CSS/JS 웹페이지 (실제 렌더링), React 컴포넌트 (실행), Mermaid 다이어그램, SVG&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에서는 아티팩트 개념이 없으며 파일을 직접 편집합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디스패치 (Dispatch)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브에이전트로 작업을 위임하는 패턴입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;병렬&lt;/b&gt;: 단일 메시지에서 여러 Task 호출 &amp;rarr; 독립 태스크에 적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순차&lt;/b&gt;: 앞 Task 결과가 다음 Task 입력일 때 &amp;mdash; 메시지를 분리해서 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;루틴 (Routine)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 4월 출시. Anthropic 클라우드 인프라에서 실행되는 스케줄 작업으로, 노트북을 닫아도 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# 스케줄링 방식
1. Cloud Routines: /routine 커맨드
2. Desktop Scheduled Tasks: 로컬 머신
3. /loop 커맨드: 세션 내 반복
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장 도구: CronCreate (5필드 cron 표현식) / CronDelete / CronList&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 사례&lt;/b&gt;: 매일 PR 리뷰, 주간 의존성 업데이트, 새벽 빌드 모니터링&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;settings.json 전체 구조&lt;/h2&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;permissions&quot;: {
    &quot;allow&quot;: [&quot;Bash(npm run *)&quot;, &quot;Bash(git commit *)&quot;, &quot;Read(**)&quot;],
    &quot;ask&quot;:   [&quot;Bash(git push *)&quot;],
    &quot;deny&quot;:  [&quot;Bash(rm -rf *)&quot;, &quot;Write(/etc/**)&quot;] 
  },
  &quot;env&quot;: {
    &quot;DEBUG&quot;: &quot;true&quot;
  },
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [{
      &quot;matcher&quot;: &quot;Bash&quot;,
      &quot;hooks&quot;: [{ &quot;type&quot;: &quot;command&quot;, &quot;command&quot;: &quot;scripts/audit.sh&quot; }]
    }]
  },
  &quot;model&quot;: &quot;claude-opus-4-7&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치 (우선순위 순): .claude/settings.local.json &amp;gt; .claude/settings.json &amp;gt; ~/.claude/settings.json&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Claude API 빠른 참조&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install anthropic
from anthropic import Anthropic

client = Anthropic()  # ANTHROPIC_API_KEY 자동 인식

# 기본 호출
message = client.messages.create(
    model=&quot;claude-opus-4-7&quot;,
    max_tokens=1024,
    system=&quot;You are a helpful coding assistant.&quot;,
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Explain async/await.&quot;}],
)
print(message.content[0].text)

# 스트리밍
with client.messages.stream(
    model=&quot;claude-opus-4-7&quot;, max_tokens=1024,
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Write a haiku&quot;}]
) as stream:
    for text in stream.text_stream:
        print(text, end=&quot;&quot;, flush=True)

# Prompt Caching (최대 90% 비용 절감)
response = client.messages.create(
    model=&quot;claude-opus-4-7&quot;, max_tokens=1024,
    system=[{
        &quot;type&quot;: &quot;text&quot;,
        &quot;text&quot;: &quot;&amp;lt;긴 정적 지시사항...&amp;gt;&quot;,
        &quot;cache_control&quot;: {&quot;type&quot;: &quot;ephemeral&quot;}  # 5분 캐시
    }],
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;질문&quot;}]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;oh-my-claudecode (OMC)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code용 멀티에이전트 오케스트레이션 레이어입니다.&lt;/p&gt;
&lt;p&gt;스킬 용도&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/oh-my-claudecode:autopilot&lt;/td&gt;
&lt;td&gt;계획 &amp;rarr; 구현 &amp;rarr; 검증 &amp;rarr; 리뷰 자동 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/oh-my-claudecode:ralph&lt;/td&gt;
&lt;td&gt;Architect 승인까지 계획/수정/검증 반복&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/oh-my-claudecode:ultrawork&lt;/td&gt;
&lt;td&gt;다중 에이전트 동시 발사, 모델 티어 라우팅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/oh-my-claudecode:team&lt;/td&gt;
&lt;td&gt;명시적 팀 오케스트레이션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/oh-my-claudecode:cancel&lt;/td&gt;
&lt;td&gt;실행 모드 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;IX. Playwright / tmux / 부수 도구&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Playwright&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Playwright는 Microsoft가 만든 오픈소스 E2E(End-to-End) 테스트 프레임워크입니다. Chromium&amp;middot;Firefox&amp;middot;WebKit 브라우저를 코드로 제어하며, AI 에이전트가 웹 브라우저를 자동화할 때도 폭넓게 활용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;크로스 브라우저&lt;/b&gt;: Chromium / Firefox / WebKit 단일 API&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동 대기(Auto-wait)&lt;/b&gt;: 요소가 안정될 때까지 자동 대기 &amp;rarr; 불안정한 sleep 불필요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 가로채기&lt;/b&gt;: 요청 모킹, 응답 스텁&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스크린샷&amp;middot;PDF&lt;/b&gt;: 전체 페이지/특정 요소 캡처&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MCP 서버&lt;/b&gt;: npx @playwright/mcp@latest &amp;mdash; Claude Code에서 브라우저 조작 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 사용 예시&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# pip install playwright &amp;amp;&amp;amp; playwright install
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()

    page.goto(&quot;&amp;lt;https://example.com&amp;gt;&quot;)
    page.fill(&quot;#search&quot;, &quot;LLM&quot;)
    page.click(&quot;button[type=submit]&quot;)
    page.wait_for_selector(&quot;.results&quot;)

    title = page.title()
    screenshot = page.screenshot(path=&quot;result.png&quot;)
    print(f&quot;페이지 제목: {title}&quot;)

    browser.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Claude Code에서 Playwright MCP 사용&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;# MCP 서버 추가
claude mcp add playwright npx @playwright/mcp@latest

# 이후 Claude가 브라우저를 직접 제어 가능
# mcp__playwright__browser_navigate, browser_click, browser_snapshot 등
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;pytest-playwright E2E 테스트 예시&lt;/h3&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;# pip install pytest-playwright
# pytest --browser chromium
import pytest
from playwright.sync_api import Page, expect

def test_login(page: Page):
    page.goto(&quot;&amp;lt;https://myapp.com/login&amp;gt;&quot;)
    page.fill(&quot;[name=email]&quot;, &quot;user@example.com&quot;)
    page.fill(&quot;[name=password]&quot;, &quot;secret&quot;)
    page.click(&quot;button[type=submit]&quot;)
    expect(page).to_have_url(&quot;&amp;lt;https://myapp.com/dashboard&amp;gt;&quot;)
    expect(page.locator(&quot;h1&quot;)).to_contain_text(&quot;대시보드&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;tmux&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tmux는 터미널 다중화(multiplexer) 도구입니다. 하나의 터미널 세션에서 여러 창(window)과 패널(pane)을 관리하고, SSH 연결이 끊겨도 세션이 유지됩니다. AI 에이전트 실습에서 백그라운드 프로세스를 관찰하거나 병렬 작업을 모니터링할 때 핵심 도구입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Session&lt;/b&gt;: tmux 최상위 단위. 여러 Window 포함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Window&lt;/b&gt;: 브라우저의 탭처럼 독립된 터미널 화면&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pane&lt;/b&gt;: 하나의 Window 안에서 분할된 터미널&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 명령어&lt;/h3&gt;
&lt;p&gt;목적 명령어&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;세션 생성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;tmux new -s mySession&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;세션 목록&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;tmux ls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;세션 재연결&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;tmux attach -t mySession&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;세션 분리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;세션 종료&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;tmux kill-session -t mySession&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;창 새로 만들기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;창 전환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; 숫자키 (0, 1, 2&amp;hellip;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;수직 분할 pane&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;수평 분할 pane&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; &quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;pane 간 이동&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ctrl+B &amp;rarr; 화살표키&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;pane 닫기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;exit 또는 Ctrl+D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;화면 캡처&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;tmux capture-pane -pt mySession&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트 실습에서의 활용&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;# 백엔드 서버 / 프론트엔드 / 에이전트 로그를 pane 3개로 분할 모니터링
tmux new -s dev
tmux split-window -h          # 우측 pane 추가
tmux split-window -v          # 우측에서 수평 추가

# Pane 0: 백엔드
tmux send-keys -t dev:0.0 'uvicorn main:app --reload' Enter

# Pane 1: 프론트엔드
tmux send-keys -t dev:0.1 'npm run dev' Enter

# Pane 2: 에이전트 로그
tmux send-keys -t dev:0.2 'tail -f agent.log' Enter
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벡터 DB 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG 파이프라인에서 임베딩 벡터를 저장하고 검색하는 데이터베이스입니다.&lt;/p&gt;
&lt;p&gt;DB 특징 호스팅 최적 사용 사례&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ChromaDB&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로컬/인메모리, 설정 최소화, Python 친화&lt;/td&gt;
&lt;td&gt;로컬 / 자체 호스팅&lt;/td&gt;
&lt;td&gt;프로토타이핑, 소규모 RAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;FAISS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Meta 오픈소스, 초고속 CPU/GPU 검색&lt;/td&gt;
&lt;td&gt;로컬 (인메모리)&lt;/td&gt;
&lt;td&gt;오프라인 대규모 배치 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Pinecone&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;완전 관리형, 실시간 업데이트, 메타데이터 필터&lt;/td&gt;
&lt;td&gt;클라우드 SaaS&lt;/td&gt;
&lt;td&gt;프로덕션 RAG, 엔터프라이즈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Weaviate&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;그래프 + 벡터 하이브리드, 멀티모달&lt;/td&gt;
&lt;td&gt;클라우드 / 자체 호스팅&lt;/td&gt;
&lt;td&gt;복잡한 지식 그래프 + 검색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;pgvector&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL 확장, SQL + 벡터 통합&lt;/td&gt;
&lt;td&gt;자체 Postgres 서버&lt;/td&gt;
&lt;td&gt;기존 Postgres 스택에 벡터 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 문제를 해결하는 대표적인 실행 패턴입니다.&lt;/p&gt;
&lt;p&gt;패턴 동작 방식 구현 도구&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ReAct&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Reasoning(추론) + Acting(행동) 반복: 생각 &amp;rarr; 도구 실행 &amp;rarr; 관찰&lt;/td&gt;
&lt;td&gt;LangChain Agent, LangGraph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Reflection&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;출력 생성 후 자기 비평 &amp;rarr; 개선 루프&lt;/td&gt;
&lt;td&gt;LangGraph (순환 그래프)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Plan-and-Execute&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;먼저 전체 계획 수립, 이후 단계별 실행&lt;/td&gt;
&lt;td&gt;LangGraph, CrewAI hierarchical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Multi-Agent&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;역할별 전문 에이전트가 협업&amp;middot;위임&lt;/td&gt;
&lt;td&gt;CrewAI, LangGraph Multi-Agent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LLM 관찰성 (Observability) 도구&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 애플리케이션의 호출 추적, 평가, 디버깅을 위한 도구입니다.&lt;/p&gt;
&lt;p&gt;도구 특징 LangChain 통합&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LangSmith&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LangChain 공식, 트레이스&amp;middot;평가&amp;middot;데이터셋 관리&lt;/td&gt;
&lt;td&gt;환경변수만 설정하면 자동 수집&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;W&amp;amp;B Weave&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Weights &amp;amp; Biases, 실험 추적 + LLM 로깅 통합&lt;/td&gt;
&lt;td&gt;weave.init() 후 자동 패치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Arize Phoenix&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오픈소스, 로컬 실행, RAG 평가 특화&lt;/td&gt;
&lt;td&gt;OpenInference 계측&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LangSmith 빠른 설정&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=&amp;lt;your-key&amp;gt;
export LANGCHAIN_PROJECT=my-rag-project

# 이후 LangChain 코드 그대로 실행 &amp;rarr; LangSmith에 자동 트레이스 수집
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기타 유용한 라이브러리&lt;/h2&gt;
&lt;p&gt;라이브러리 역할 주요 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LlamaIndex&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터-LLM 연결 RAG 프레임워크&lt;/td&gt;
&lt;td&gt;대규모 데이터 인덱싱, 쿼리 엔진&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Pydantic AI&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;구조화된 LLM 출력 + 에이전트&lt;/td&gt;
&lt;td&gt;타입 안전 결과, 유효성 검사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Instructor&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LLM 응답을 Pydantic 모델로 파싱&lt;/td&gt;
&lt;td&gt;response_model 파라미터 하나로 구조화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Semantic Kernel&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Microsoft의 LLM 오케스트레이션 SDK&lt;/td&gt;
&lt;td&gt;C# / Python / Java, Azure AI 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>공부</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/715</guid>
      <comments>https://brownbears.tistory.com/715#entry715comment</comments>
      <pubDate>Mon, 27 Apr 2026 02:25:18 +0900</pubDate>
    </item>
    <item>
      <title>[Spark] 카탈리스크 옵티마이저</title>
      <link>https://brownbears.tistory.com/748</link>
      <description>&lt;h1&gt;1. 개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Spark의 쿼리 실행 성능을 결정하는 핵심 엔진이 바로 &lt;b&gt;카탈리스트 옵티마이저(Catalyst Optimizer)&lt;/b&gt;입니다. Spark SQL은 사용자가 작성한 SQL 쿼리나 DataFrame/Dataset API 코드를 내부적으로 최적의 실행 계획으로 변환하는데, 이 과정 전체를 카탈리스트 옵티마이저가 담당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈리스트 옵티마이저는 Spark 1.3(2014년)에 처음 도입되었으며, Scala의 함수형 프로그래밍 특성을 적극 활용하여 확장 가능한 구조로 설계되었습니다. 규칙 기반(Rule-Based) 최적화와 비용 기반(Cost-Based) 최적화를 모두 지원하며, Parquet&amp;middot;Hive&amp;middot;JDBC 등 다양한 데이터 소스와의 통합도 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 &lt;b&gt;&quot;무엇을(What)&quot;&lt;/b&gt; 쿼리할지만 기술하면 되고, &lt;b&gt;&quot;어떻게(How)&quot;&lt;/b&gt; 실행할지는 카탈리스트가 자동으로 결정합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.databricks.com/sites/default/files/2015/04/Screen-Shot-2015-04-12-at-8.11.11-AM.png&quot; alt=&quot;Catalyst Optimizer 4단계 파이프라인 개요 (출처: Databricks)&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Catalyst Optimizer 4단계 파이프라인 개요 (출처: Databricks)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 카탈리스트 옵티마이저 동작 원리&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 핵심 개념: 트리 변환 (Tree Transformation)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈리스트 내부는 모든 쿼리 표현을 &lt;b&gt;트리(Tree) 구조&lt;/b&gt;로 표현합니다. 모든 노드는 불변(Immutable) 객체이며, 최적화는 이 트리에 변환 규칙을 반복 적용하는 방식으로 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 노드 유형:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Literal(value)&lt;/code&gt; &amp;mdash; 상수값&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Attribute(name)&lt;/code&gt; &amp;mdash; 입력 행의 속성(컬럼)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Add(left, right)&lt;/code&gt; &amp;mdash; 두 표현식의 합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환 규칙 예시:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;tree.transform {
  case Add(Literal(c1), Literal(c2)) =&amp;gt; Literal(c1 + c2)
  case Add(left, Literal(0)) =&amp;gt; left
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;트리 변환 전/후 시각화 (x + 1 + 2 예시):&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;변환 전:                    변환 후:
      Add                       Add
     /   \          &amp;rarr;          /   \
   Add   Lit(2)            x(Attr)  Lit(3)
  /   \
x(Attr) Lit(1)
&amp;rarr; Add(Lit(1), Lit(2)) = Lit(3) 으로 상수 폴딩&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://www.databricks.com/wp-content/uploads/2015/04/Screen-Shot-2015-04-12-at-8.41.26-AM.png&quot; alt=&quot;Catalyst 트리 변환 다이어그램 (출처: Databricks)&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Catalyst 트리 변환 다이어그램 (출처: Databricks)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙들은 &lt;b&gt;배치(Batch)&lt;/b&gt;로 묶여 트리에 변화가 없을 때까지 반복(고정점, Fixed-Point) 실행됩니다. Spark 2.4.7 기준 &lt;b&gt;25개 배치, 109개 규칙(고유 69개)&lt;/b&gt;이 존재합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 4단계 파이프라인&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;SQL / DataFrame API
        &amp;darr;
┌──────────────────────────────────────────────────┐
│  1. Analysis                                     │
│     Unresolved Logical Plan &amp;rarr; Analyzed LP        │
│     (컬럼명&amp;middot;타입 해석, Catalog 조회)              │
└───────────────────┬──────────────────────────────┘
                    &amp;darr;
┌──────────────────────────────────────────────────┐
│  2. Logical Optimization                         │
│     Analyzed LP &amp;rarr; Optimized LP                   │
│     (Predicate Pushdown, Constant Folding 등)    │
└───────────────────┬──────────────────────────────┘
                    &amp;darr;
┌──────────────────────────────────────────────────┐
│  3. Physical Planning                            │
│     Optimized LP &amp;rarr; SparkPlan                     │
│     (BroadcastHashJoin vs SortMergeJoin 선택)    │
└───────────────────┬──────────────────────────────┘
                    &amp;darr;
┌──────────────────────────────────────────────────┐
│  4. Code Generation                              │
│     SparkPlan &amp;rarr; Java Bytecode                    │
│     (Whole-Stage CodeGen, Janino 컴파일)         │
└───────────────────┬──────────────────────────────┘
                    &amp;darr;
              RDD 실행&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Analysis (분석 단계)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 파서 또는 DataFrame API로 생성된 &lt;b&gt;Unresolved Logical Plan&lt;/b&gt;을 &lt;b&gt;Analyzed Logical Plan&lt;/b&gt;으로 변환합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;df = spark.sql(&quot;SELECT name, age FROM employees WHERE dept = 'Engineering'&quot;)

# Unresolved Logical Plan (분석 전) &amp;mdash; 모든 참조가 미해결 상태
# Project [unresolvedAttr(name), unresolvedAttr(age)]
# └── Filter unresolvedAttr(dept) = 'Engineering'
#     └── UnresolvedRelation employees

# Analyzed Logical Plan (분석 후) &amp;mdash; Catalog에서 해석 완료
# Project [name#10, age#11]
# └── Filter (dept#12 = 'Engineering')
#     └── Relation employees [name#10, age#11, dept#12, salary#13]
#                                        ^^^^ 고유 ID 부여됨&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Logical Optimization (논리 최적화 단계)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석이 완료된 논리 계획에 &lt;b&gt;규칙 기반 최적화&lt;/b&gt;를 적용하여 &lt;b&gt;Optimized Logical Plan&lt;/b&gt;을 생성합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;규칙&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Constant Folding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1+2&lt;/code&gt; &amp;rarr; &lt;code&gt;3&lt;/code&gt;, 컴파일 시점에 상수 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predicate Pushdown&lt;/td&gt;
&lt;td&gt;필터를 데이터 소스 방향으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projection Pruning&lt;/td&gt;
&lt;td&gt;필요 없는 컬럼 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combine Filters&lt;/td&gt;
&lt;td&gt;인접한 필터 조건 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Null Propagation&lt;/td&gt;
&lt;td&gt;Null 값 처리 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boolean Simplification&lt;/td&gt;
&lt;td&gt;불린 연산 단순화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OptimizeIn&lt;/td&gt;
&lt;td&gt;단일 원소 IN 리스트를 등호 비교로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;df = spark.read.parquet(&quot;/data/employees&quot;) \
    .filter(&quot;age &amp;gt; 20&quot;).filter(&quot;age &amp;gt; 30&quot;) \
    .select(&quot;name&quot;, &quot;age&quot;, &quot;salary&quot;)

df.explain(True)

# ── Optimized Logical Plan ──
# Project [name#10, age#11]          &amp;larr; salary 제거 (Projection Pruning)
# └── Filter (age#11 &amp;gt; 30)           &amp;larr; 두 Filter 병합 + 약한 조건 제거
#     └── Relation employees&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Physical Planning (물리 계획 단계)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화된 논리 계획을 Spark 실행 엔진이 수행할 &lt;b&gt;SparkPlan&lt;/b&gt;으로 변환합니다. SparkPlanner가 10가지 전략으로 물리 연산자를 매핑하고, CBO로 최적 계획을 선택합니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;orders = spark.read.parquet(&quot;/data/orders&quot;)
customers = spark.read.parquet(&quot;/data/customers&quot;)  # 소규모 (10MB 이하)

orders.join(customers, &quot;customer_id&quot;).filter(&quot;order_date &amp;gt; '2024-01-01'&quot;).explain()

# ── Physical Plan ──
# *(2) BroadcastHashJoin [customer_id#2], [customer_id#21]
#    :- *(2) Filter (order_date#4 &amp;gt; 2024-01-01)
#    :  +- *(2) FileScan parquet orders
#    +- BroadcastExchange HashedRelationBroadcastMode  &amp;larr; 소규모 테이블 브로드캐스트
#       +- *(1) FileScan parquet customers&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code Generation (코드 생성 단계)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Whole-Stage Code Generation&lt;/b&gt;: 여러 물리 연산자를 단일 Java 함수로 묶어 컴파일합니다. &lt;code&gt;*(1)&lt;/code&gt; 프리픽스가 단일 코드젠 스테이지를 의미합니다. Janino 컴파일러가 런타임에 바이트코드를 생성합니다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;df = spark.range(1_000_000).selectExpr(&quot;id * 2 + 1 as value&quot;).filter(&quot;value &amp;gt; 100&quot;)
df.explain()

# *(1) Filter (((id#0L * 2) + 1) &amp;gt; 100)   &amp;larr; 두 연산자가
# +- *(1) Range (0, 1000000, step=1)      &amp;larr; 단일 Java 함수로 컴파일됨
#
# for (long id = 0; id &amp;lt; 1000000; id++) {
#   long value = id * 2 + 1;
#   if (value &amp;gt; 100) emit(value);  &amp;larr; 인라인 처리, 함수 호출 오버헤드 없음
# }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Code Generation 성능 비교 (출처: Databricks):&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.databricks.com/wp-content/uploads/2015/04/Screen-Shot-2015-04-12-at-8.45.27-AM-300x129.png&quot; alt=&quot;Code Generation 성능 비교 &amp;mdash; Volcano 모델 vs Whole-Stage CodeGen&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code Generation 성능 비교 &amp;mdash; Volcano 모델 vs Whole-Stage CodeGen&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. I/O 최적화 전략&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I/O 최적화는 카탈리스트가 제공하는 가장 강력한 성능 개선 수단입니다. 3개 계층이 함께 동작하면 쿼리가 실제로 읽는 데이터를 전체의 &lt;b&gt;1% 미만&lt;/b&gt;으로 줄일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3계층 I/O 최적화:

┌─────────────────────────────────────────────────────────┐
│  Layer 1: Partition Pruning                             │
│  디렉토리 레벨 &amp;mdash; 관련 없는 파티션 폴더 자체를 스킵     │
│  예) dt=2025-01-15/ 만 접근, 나머지 364개 폴더 무시    │
├─────────────────────────────────────────────────────────┤
│  Layer 2: Data Skipping (File-level)                    │
│  파일 레벨 &amp;mdash; min/max 통계로 전체 파일 스킵             │
│  예) file.parquet min=100, max=200 &amp;rarr; value=50 조건 스킵 │
├─────────────────────────────────────────────────────────┤
│  Layer 3: Predicate Pushdown + Column Pruning           │
│  행&amp;middot;컬럼 레벨 &amp;mdash; 조건 행만 읽고, 필요 컬럼만 읽음      │
│  예) PushedFilters, ReadSchema 컬럼 제한               │
└─────────────────────────────────────────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 Predicate Pushdown (술어 푸시다운)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터 조건을 데이터 소스 레벨로 이동시켜 조건을 만족하지 않는 행은 처음부터 읽지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;푸시다운 없음:                         푸시다운 적용:

┌───────────────┐                    ┌───────────────┐
│  Spark Filter │ &amp;larr; 1억 건 처리      │  Spark        │ &amp;larr; 10만 건만 처리
└───────┬───────┘                    └───────┬───────┘
        │ 1억 건 전송                         │ 10만 건만 전송
┌───────┴───────┐                    ┌───────┴───────┐
│  Parquet      │ &amp;rarr; 전체 스캔        │  Parquet      │ &amp;rarr; 조건 행만 반환
└───────────────┘                    └───────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;df = spark.read.parquet(&quot;/data/sales&quot;) \
    .filter(&quot;region = 'KR' AND amount &amp;gt; 10000&quot;) \
    .select(&quot;product_id&quot;, &quot;amount&quot;)

df.explain()

# *(1) FileScan parquet [product_id#5, amount#7, region#9]
#      PushedFilters: [IsNotNull(region), EqualTo(region,KR),
#                      IsNotNull(amount), GreaterThan(amount,10000)]
#      ReadSchema: struct&amp;lt;product_id:string, amount:long&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지원 포맷:&lt;/b&gt; Parquet, ORC, JDBC, Delta Lake 등 FileScan 기반 소스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;푸시다운이 작동하지 않는 경우:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UDF 사용 시 &amp;mdash; 옵티마이저가 내부 로직을 분석할 수 없음&lt;/li&gt;
&lt;li&gt;복잡한 Window 함수 &amp;mdash; 일부 제한 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;# ❌ UDF &amp;rarr; PushedFilters: []  (전체 로드 후 처리)
df.filter(my_udf(df.age)).explain()

# ✅ 내장 함수 &amp;rarr; PushedFilters: [GreaterThan(age,60)]
df.filter(df.age &amp;gt; 60).explain()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 Column Pruning (컬럼 제거)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 결과에 필요한 컬럼만 읽고 나머지는 I/O 단계에서 완전히 제외합니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 원본 테이블: employees(id, name, age, salary, dept, phone, address, join_date)
df = spark.read.parquet(&quot;/data/employees&quot;).select(&quot;id&quot;, &quot;name&quot;)
df.explain()

# *(1) FileScan parquet [id#1, name#2]
#      ReadSchema: struct&amp;lt;id:int, name:string&amp;gt;
#      &amp;rarr; 나머지 6개 컬럼은 디스크에서 아예 읽지 않음 (I/O 75% 절감)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet&amp;middot;ORC 같은 컬럼 단위 저장 포맷과 결합 시 I/O 절감 효과가 극대화됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.3 Partition Pruning (파티션 프루닝)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 디렉토리 자체를 접근하지 않는 기법입니다. Predicate Pushdown이 파일 내부의 행을 필터링한다면, 파티션 프루닝은 &lt;b&gt;디렉토리 자체&lt;/b&gt;를 건너뜁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Static Partition Pruning&lt;/b&gt; &amp;mdash; 쿼리 컴파일 시점에 파티션 결정:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# 파티션 구조: /data/sales/dt=2025-01-01/ ~ dt=2025-12-31/ (365개)
df = spark.read.parquet(&quot;/data/sales&quot;)
df.filter(&quot;dt = '2025-01-15'&quot;).explain()

# *(1) FileScan parquet [amount#1, dt#2]
#      PartitionFilters: [isnotnull(dt#2), (dt#2 = 2025-01-15)]  &amp;larr; 프루닝 적용
#      &amp;rarr; dt=2025-01-15/ 하나만 접근 (364개 파티션 스킵)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dynamic Partition Pruning (DPP, Spark 3.0+)&lt;/b&gt; &amp;mdash; 조인 시 런타임에 파티션 결정:&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.optimizer.dynamicPartitionPruning.enabled&quot;, &quot;true&quot;)

fact = spark.read.parquet(&quot;/data/sales&quot;)       # 파티션: dt
dim  = spark.read.parquet(&quot;/data/promotions&quot;)  # 소규모 차원 테이블

result = fact.join(dim, &quot;campaign_id&quot;).filter(&quot;dim.region = 'KR'&quot;)
result.explain()

# FileScan parquet sales
#   PartitionFilters: [dynamicpruningexpression(dt#2 IN subquery#1)]  &amp;larr; DPP
#   &amp;rarr; dim의 KR 캠페인 날짜에 해당하는 파티션만 런타임에 스캔&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.4 Data Skipping (데이터 스키핑)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet 파일은 각 Row Group(기본 128MB)마다 컬럼별 &lt;b&gt;min/max 통계&lt;/b&gt;를 저장합니다. Spark은 이 통계를 읽어 조건을 만족할 수 없는 Row Group 전체를 스킵합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Parquet 파일 내부:

┌──────────────────────────────────────┐
│  Row Group 1  │ age: min=18, max=35  │ &amp;larr; age &amp;gt; 80 &amp;rarr; 스킵 (max=35 &amp;lt; 80)
│  Row Group 2  │ age: min=36, max=60  │ &amp;larr; age &amp;gt; 80 &amp;rarr; 스킵 (max=60 &amp;lt; 80)
│  Row Group 3  │ age: min=61, max=85  │ &amp;larr; age &amp;gt; 80 &amp;rarr; 읽음 (max=85 &amp;gt;= 80)
└──────────────────────────────────────┘
&amp;rarr; 3개 중 1개만 스캔 (67% I/O 절감)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;df = spark.read.parquet(&quot;/data/employees&quot;)
df.filter(&quot;age &amp;gt; 80&quot;).explain()

# *(1) FileScan parquet [age#1, name#2]
#      PushedFilters: [IsNotNull(age), GreaterThan(age,80)]
#      RowGroups: 3 out of 10 read   &amp;larr; 10개 Row Group 중 3개만 읽음
# 설정: spark.sql.parquet.filterPushdown = true (기본값)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Delta Lake Data Skipping + Z-Order:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# Delta Lake: 트랜잭션 로그(_delta_log/)에 파일별 min/max 통계 자동 저장 (기본 32컬럼)

# Z-Order로 같은 값의 데이터를 같은 파일에 클러스터링 &amp;rarr; min/max 범위 최소화
spark.sql(&quot;&quot;&quot;
    OPTIMIZE delta.`/data/events`
    ZORDER BY (user_id, event_date)
&quot;&quot;&quot;)
# &amp;rarr; user_id 기반 조회 시 대부분의 파일 min/max 범위 밖 &amp;rarr; 스킵 가능&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 조인 &amp;amp; 실행 최적화&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 Broadcast Join (브로드캐스트 조인)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽 테이블이 충분히 작을 경우, 셔플 없이 작은 테이블을 모든 노드에 복사(broadcast)하여 조인합니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;from pyspark.sql.functions import broadcast

orders = spark.read.parquet(&quot;/data/orders&quot;)        # 10억 건
country_codes = spark.read.parquet(&quot;/data/codes&quot;)  # 200건 (소규모)

# 자동 감지 (10MB 이하 &amp;rarr; 자동 브로드캐스트)
spark.conf.set(&quot;spark.sql.autoBroadcastJoinThreshold&quot;, &quot;10m&quot;)

# 명시적 강제 적용
orders.join(broadcast(country_codes), &quot;country_code&quot;).explain()

# BroadcastHashJoin vs SortMergeJoin
# ┌──────────────────────┬──────────────────────┐
# │  BroadcastHashJoin   │   SortMergeJoin      │
# ├──────────────────────┼──────────────────────┤
# │ 네트워크 셔플 없음   │ 전체 데이터 셔플     │
# │ 소규모 테이블 필요   │ 테이블 크기 무관     │
# │ 훨씬 빠름            │ 대규모 조인에 안정적 │
# └──────────────────────┴──────────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2 Join Reordering &amp;amp; CBO (조인 재정렬 &amp;amp; 비용 기반 최적화)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CBO는 데이터 통계(행 수, 컬럼 분포, NDV 등)를 바탕으로 여러 실행 계획 중 비용이 가장 낮은 계획을 선택합니다. 조인 재정렬에 주로 활용됩니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.cbo.enabled&quot;, &quot;true&quot;)

# 통계 수집
spark.sql(&quot;ANALYZE TABLE orders COMPUTE STATISTICS FOR ALL COLUMNS&quot;)
spark.sql(&quot;ANALYZE TABLE products COMPUTE STATISTICS FOR ALL COLUMNS&quot;)

# 수집된 통계 확인
spark.sql(&quot;DESCRIBE EXTENDED my_table&quot;).show(truncate=False)
# Statistics: 1234567890 bytes, 10000000 rows
# col_stats: age &amp;rarr; min:18, max:80, ndv:62 | region &amp;rarr; ndv:8

# CBO 비활성화: 작성 순서 그대로
orders.join(customers, &quot;cid&quot;).join(products, &quot;pid&quot;).explain()
# SortMergeJoin orders &amp;times; customers &amp;rarr; 결과 &amp;times; products (비효율)

# CBO 활성화: 통계 기반 최적 순서
orders.join(customers, &quot;cid&quot;).join(products, &quot;pid&quot;).explain()
# BroadcastHashJoin products(소규모 먼저) &amp;rarr; 결과 &amp;times; orders  &amp;larr; 재정렬&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.3 Adaptive Query Execution (AQE, Spark 3.0+)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임에 수집한 실측 통계를 기반으로 실행 중 계획을 동적으로 재최적화합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기능&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;파티션 동적 조정&lt;/td&gt;
&lt;td&gt;초기 200 파티션 &amp;rarr; 실제 데이터 기반 15개로 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;조인 전략 변경&lt;/td&gt;
&lt;td&gt;Sort-Merge &amp;rarr; Broadcast (런타임 크기 확인 후)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스큐 조인 처리&lt;/td&gt;
&lt;td&gt;특정 키에 데이터가 몰려도 자동 파티션 분할&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)          # Spark 3.2+에서 기본 true
spark.conf.set(&quot;spark.sql.adaptive.coalescePartitions.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.enabled&quot;, &quot;true&quot;)

df = spark.read.parquet(&quot;/data/events&quot;).groupBy(&quot;event_type&quot;).agg({&quot;user_id&quot;: &quot;count&quot;})
df.explain()

# AdaptiveSparkPlan isFinalPlan=false    &amp;larr; AQE 활성화 표시
# +- HashAggregate
#    +- Exchange hashpartitioning(event_type, 200)  &amp;larr; 초기 200 파티션
#
# 실행 후: 실제 데이터 있는 15개로 자동 병합 (빈 태스크 185개 제거)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 실무 주의사항: 파티션 키와 함수&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.1 파티션 키에 함수를 감싸면 안 되는 이유 (오픈소스 Spark)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파티션 키 컬럼을 함수로 감싸면 파티션 프루닝이 완전히 무력화됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵티마이저가 파티션 프루닝을 적용하려면 컴파일 시점에 어느 파티션 디렉토리를 읽을지 결정해야 합니다. &lt;code&gt;to_date(dt) = '2025-01-01'&lt;/code&gt;이라고 쓰면, 옵티마이저 입장에서는 &lt;b&gt;dt의 모든 값을 실제로 읽어서 함수를 적용해 봐야&lt;/b&gt; 결과가 맞는지 알 수 있습니다. 따라서 프루닝을 포기하고 전체 파티션을 스캔합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;파티션 디렉토리: /data/logs/dt=2025-01-01/ ~ dt=2025-12-31/ (365개)

[BAD] to_date(dt) = '2025-01-01'
  &amp;rarr; 옵티마이저: 어느 dt= 폴더를 써야 하는지 알 수 없음
  &amp;rarr; 전체 365개 파티션 스캔 후 함수 적용

[GOOD] dt = '2025-01-01'
  &amp;rarr; 옵티마이저: dt=2025-01-01 폴더만 직접 접근
  &amp;rarr; 1개 파티션만 스캔&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;❌ 프루닝 무력화 패턴 vs ✅ 올바른 패턴:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- ❌ 함수 적용 &amp;rarr; PartitionFilters: [] (전체 스캔)
WHERE to_date(dt) = '2025-01-01'
WHERE datetime(dt) &amp;gt;= '2025-01-01 00:00:00'
WHERE date_format(dt, 'yyyy-MM') = '2025-01'
WHERE YEAR(dt) = 2025 AND MONTH(dt) = 1

-- ✅ 직접 비교 &amp;rarr; PartitionFilters 적용 (스킵)
WHERE dt = '2025-01-01'
WHERE dt &amp;gt;= '2025-01-01' AND dt &amp;lt; '2025-01-02'
WHERE dt BETWEEN '2025-01-01' AND '2025-01-31'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EXPLAIN으로 차이 확인:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;df = spark.read.parquet(&quot;/data/logs&quot;)  # dt 컬럼으로 파티션됨

# ❌ 잘못된 패턴
from pyspark.sql.functions import to_date
df.filter(to_date(df.dt) == '2025-01-01').explain()
# FileScan parquet
#   PartitionFilters: []   &amp;larr; 비어 있음! 전체 파티션 스캔

# ✅ 올바른 패턴
df.filter(&quot;dt = '2025-01-01'&quot;).explain()
# FileScan parquet
#   PartitionFilters: [isnotnull(dt#1), (dt#1 = 2025-01-01)]  &amp;larr; 프루닝 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Z-Order 클러스터링에서도 동일하게 적용됩니다:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;-- ❌ 함수 적용 &amp;rarr; min/max 통계 비교 불가 &amp;rarr; 데이터 스키핑 안 됨
WHERE CAST(user_id AS STRING) = '12345'

-- ✅ 직접 비교 &amp;rarr; min/max 통계 활용 &amp;rarr; 대부분 파일 스킵
WHERE user_id = 12345&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.2 Databricks Delta Lake의 자동 해법: Generated Columns&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 문제를 Databricks Delta Lake는 Generated Columns(생성 컬럼)으로 자동 해결합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generated Column은 다른 컬럼의 함수로 자동 계산되는 특수 컬럼입니다. 파티션 컬럼을 Generated Column으로 정의하면, Delta Lake가 &lt;b&gt;기본 컬럼으로 필터링해도 파티션 필터를 자동 생성&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동작 원리:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;[테이블 스키마]
  event_time  TIMESTAMP          &amp;larr; 실제 데이터 컬럼
  event_date  DATE (Generated)   &amp;larr; CAST(event_time AS DATE) 로 자동 계산

[쿼리]
  WHERE event_time &amp;gt;= '2025-01-01'

[Delta Lake 내부 처리]
  event_time과 event_date의 관계를 알고 있음
  &amp;rarr; event_date = '2025-01-01' 파티션 필터 자동 추가
  &amp;rarr; 해당 파티션만 스캔&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;생성 컬럼으로 테이블 정의:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE events (
  event_id   BIGINT,
  event_time TIMESTAMP,
  user_id    BIGINT,
  -- Generated Column: event_time으로부터 자동 계산, 직접 삽입 불필요
  event_date DATE GENERATED ALWAYS AS (CAST(event_time AS DATE))
)
USING DELTA
PARTITIONED BY (event_date);

-- 쓰기 시: event_date를 직접 지정할 필요 없음 (Delta가 자동 계산)
INSERT INTO events (event_id, event_time, user_id)
VALUES (1, '2025-01-15 14:30:00', 12345);
-- &amp;rarr; event_date = '2025-01-15' 자동 저장&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 컬럼으로 필터링해도 파티션 프루닝 자동 적용:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- event_date 조건 없이 event_time으로만 필터링
SELECT * FROM events
WHERE event_time &amp;gt;= '2025-01-01'
  AND event_time &amp;lt;  '2025-01-02';

-- Delta Lake 내부 처리:
-- event_time &amp;gt;= '2025-01-01' &amp;rarr; event_date = '2025-01-01' 자동 추가
-- &amp;rarr; EXPLAIN 출력: PartitionFilters: [(event_date = 2025-01-01)]
-- &amp;rarr; event_date=2025-01-01/ 파티션 하나만 스캔&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지원 표현식 목록:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;표현식&lt;/th&gt;
&lt;th&gt;컬럼 타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CAST(col AS DATE)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;타임스탬프 &amp;rarr; 날짜 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YEAR(col)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;연도 추출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YEAR(col)&lt;/code&gt; &amp;bull; &lt;code&gt;MONTH(col)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;연&amp;middot;월 파티션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YEAR(col)&lt;/code&gt; &amp;bull; &lt;code&gt;MONTH(col)&lt;/code&gt; &amp;bull; &lt;code&gt;DAY(col)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;연&amp;middot;월&amp;middot;일 파티션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YEAR(col)&lt;/code&gt; &amp;bull; &lt;code&gt;MONTH(col)&lt;/code&gt; &amp;bull; &lt;code&gt;DAY(col)&lt;/code&gt; &amp;bull; &lt;code&gt;HOUR(col)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;시간 단위 파티션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DATE_FORMAT(col, 'yyyy-MM')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;월별 파티션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SUBSTRING(col, pos, len)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;STRING&lt;/td&gt;
&lt;td&gt;문자열 부분 파티션&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Photon 요구사항:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Databricks Runtime 10.4 LTS 이하 &amp;rarr; Photon 필수&lt;/li&gt;
&lt;li&gt;Databricks Runtime 11.3 LTS 이상 &amp;rarr; Photon 불필요 (자동 적용)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오픈소스 Spark vs Databricks 비교:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;오픈소스 Spark&lt;/th&gt;
&lt;th&gt;Databricks Delta Lake&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;to_date(dt) = '2025-01-01'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PartitionFilters: [] (전체 스캔)&lt;/td&gt;
&lt;td&gt;Generated Column 설정 시 자동 프루닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YEAR(dt) = 2025&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;프루닝 불가&lt;/td&gt;
&lt;td&gt;Generated Column 설정 시 자동 프루닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dt = '2025-01-01'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정상 프루닝&lt;/td&gt;
&lt;td&gt;정상 프루닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설정 필요 여부&lt;/td&gt;
&lt;td&gt;없음 (직접 비교만 동작)&lt;/td&gt;
&lt;td&gt;테이블 생성 시 Generated Column 정의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.3 다중 파티션 키 (ym + dt) 와 프루닝 범위&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 파티션을 &lt;code&gt;ym(연월)&lt;/code&gt; + &lt;code&gt;dt(날짜)&lt;/code&gt; 두 계층으로 구성하는 경우가 많습니다. 이 때 &lt;code&gt;WHERE dt = '2025-01-15'&lt;/code&gt; 조건만 사용하면 상위 파티션 &lt;code&gt;ym&lt;/code&gt;까지 프루닝되는지는 &lt;b&gt;오픈소스 Spark와 Databricks 간에 동작이 다릅니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일시스템 계층 구조 (ym + dt 2단계 파티션):&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/data/logs/
  ym=2025-01/
    dt=2025-01-01/  &amp;larr; part-00000.parquet
    dt=2025-01-15/  &amp;larr; part-00000.parquet  (찾고자 하는 파티션)
    dt=2025-01-31/
  ym=2025-02/
    dt=2025-02-01/
    ...
  ym=2025-12/
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오픈소스 Spark &amp;mdash; dt 조건만으로는 ym 프루닝 안 됨:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 Spark는 &lt;code&gt;ym&lt;/code&gt;과 &lt;code&gt;dt&lt;/code&gt; 사이의 &lt;b&gt;논리적 관계를 모릅니다.&lt;/b&gt; &lt;code&gt;dt = '2025-01-15'&lt;/code&gt;라고 써도 옵티마이저는 해당 dt가 어느 &lt;code&gt;ym=&lt;/code&gt; 폴더 아래 있는지 추론할 수 없습니다. 결국 &lt;b&gt;모든&lt;/b&gt; &lt;code&gt;ym=*/&lt;/code&gt; 디렉토리를 열어 안에 있는 &lt;code&gt;dt=&lt;/code&gt; 서브파티션을 하나씩 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;df = spark.read.parquet(&quot;/data/logs&quot;)  # PARTITIONED BY (ym, dt)

# ❌ dt만 명시 &amp;rarr; ym 전체 스캔
df.filter(&quot;dt = '2025-01-15'&quot;).explain()
# FileScan parquet
#   PartitionFilters: [isnotnull(dt#2), (dt#2 = 2025-01-15)]
#   &amp;larr; dt 만 프루닝. ym은 비어있어 12개 ym 폴더 모두 진입
#   &amp;rarr; 실제로는 ym=2025-01/dt=2025-01-15/ 1개만 필요하지만
#      ym=2025-02/ ~ ym=2025-12/ 도 모두 열어서 dt 확인 후 버림

# ✅ ym 까지 명시 &amp;rarr; 두 레벨 모두 프루닝
df.filter(&quot;ym = '2025-01' AND dt = '2025-01-15'&quot;).explain()
# PartitionFilters: [(ym#1 = 2025-01), (dt#2 = 2025-01-15)]  &amp;larr; 두 레벨 동시 적용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Databricks Delta Lake &amp;mdash; Generated Column으로 ym 자동 프루닝:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ym&lt;/code&gt;을 &lt;code&gt;dt&lt;/code&gt;의 Generated Column으로 정의하면 Delta Lake가 두 컬럼의 관계를 인식하여, &lt;code&gt;dt&lt;/code&gt; 조건만 있어도 상위 파티션 &lt;code&gt;ym&lt;/code&gt;을 &lt;b&gt;자동으로 프루닝&lt;/b&gt;합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE logs (
  log_id   BIGINT,
  dt       DATE,
  -- ym 은 dt 로부터 자동 계산되는 Generated Column
  ym       STRING GENERATED ALWAYS AS (DATE_FORMAT(dt, 'yyyy-MM')),
  message  STRING
)
USING DELTA
PARTITIONED BY (ym, dt);

-- dt 조건만 사용 &amp;rarr; Delta Lake 가 ym 파티션 필터 자동 추가
SELECT * FROM logs WHERE dt = '2025-01-15';
-- Delta Lake 내부:
--   dt = '2025-01-15'  &amp;rarr;  ym = '2025-01' 자동 추론
--   PartitionFilters: [(ym = '2025-01'), (dt = '2025-01-15')]
--   &amp;rarr; ym=2025-01/dt=2025-01-15/ 1개 파티션만 스캔&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오픈소스 Spark vs Databricks &amp;mdash; 다중 파티션 키 프루닝 비교:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필터 조건&lt;/th&gt;
&lt;th&gt;오픈소스 Spark&lt;/th&gt;
&lt;th&gt;Databricks Delta Lake&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE dt = '2025-01-15'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;dt만 프루닝, &lt;b&gt;ym 전체 스캔&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Generated Column 정의 시 &lt;b&gt;ym도 자동 프루닝&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE ym='2025-01' AND dt='2025-01-15'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;두 레벨 모두 프루닝&lt;/td&gt;
&lt;td&gt;두 레벨 모두 프루닝&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상위 파티션 자동 추론&lt;/td&gt;
&lt;td&gt;불가 (독립적으로 처리)&lt;/td&gt;
&lt;td&gt;dt &amp;rarr; ym 관계 인식 (Generated Column)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설정 필요 여부&lt;/td&gt;
&lt;td&gt;없음 (명시 조건만 동작)&lt;/td&gt;
&lt;td&gt;테이블 생성 시 Generated Column 정의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; 오픈소스 Spark에서 계층형 다중 파티션을 사용할 때, 상위 파티션(ym)을 프루닝하려면 WHERE 절에 &lt;b&gt;반드시 명시&lt;/b&gt;해야 합니다. Databricks Delta Lake는 Generated Column 정의만으로 이 제약을 자동으로 해결합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 요약&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈리스트 옵티마이저는 Apache Spark의 쿼리 최적화 핵심 엔진으로, 사용자의 쿼리를 최적의 실행 계획으로 변환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4단계 파이프라인:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단계&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Analysis&lt;/td&gt;
&lt;td&gt;이름&amp;middot;타입 해석&lt;/td&gt;
&lt;td&gt;규칙 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logical Optimization&lt;/td&gt;
&lt;td&gt;논리 계획 단순화&lt;/td&gt;
&lt;td&gt;규칙 기반 (25배치, 69규칙)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Physical Planning&lt;/td&gt;
&lt;td&gt;물리 연산자 매핑&lt;/td&gt;
&lt;td&gt;규칙 + 비용 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Generation&lt;/td&gt;
&lt;td&gt;Java 바이트코드 컴파일&lt;/td&gt;
&lt;td&gt;Whole-Stage CodeGen&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 최적화 기법:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기법&lt;/th&gt;
&lt;th&gt;계층&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Predicate Pushdown&lt;/td&gt;
&lt;td&gt;행 레벨&lt;/td&gt;
&lt;td&gt;조건 행만 읽음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column Pruning&lt;/td&gt;
&lt;td&gt;컬럼 레벨&lt;/td&gt;
&lt;td&gt;필요 컬럼만 읽음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition Pruning&lt;/td&gt;
&lt;td&gt;디렉토리 레벨&lt;/td&gt;
&lt;td&gt;관련 파티션 폴더만 접근&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Skipping&lt;/td&gt;
&lt;td&gt;Row Group 레벨&lt;/td&gt;
&lt;td&gt;min/max 통계로 블록 스킵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broadcast Join&lt;/td&gt;
&lt;td&gt;조인 전략&lt;/td&gt;
&lt;td&gt;소규모 테이블 셔플 없이 복사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CBO&lt;/td&gt;
&lt;td&gt;계획 선택&lt;/td&gt;
&lt;td&gt;통계 기반 최소 비용 계획 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AQE&lt;/td&gt;
&lt;td&gt;런타임 재최적화&lt;/td&gt;
&lt;td&gt;실측 기반 동적 조정 (Spark 3.0+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실무 핵심 주의사항:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션/클러스터링 키를 &lt;code&gt;to_date()&lt;/code&gt;, &lt;code&gt;datetime()&lt;/code&gt;, &lt;code&gt;YEAR()&lt;/code&gt; 등 함수로 감싸면 Partition Pruning과 Data Skipping이 &lt;b&gt;완전히 무력화&lt;/b&gt;됩니다. 직접 리터럴 비교를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;b&gt;Databricks Delta Lake&lt;/b&gt;는 테이블 생성 시 &lt;b&gt;Generated Columns&lt;/b&gt;를 정의하면, 기본 컬럼에 함수를 적용한 필터에서도 파티션 프루닝을 &lt;b&gt;자동으로 처리&lt;/b&gt;합니다 (DBR 11.3 LTS 이상, Photon 불필요).&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 레퍼런스&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html&quot;&gt;Deep Dive into Spark SQL's Catalyst Optimizer &amp;mdash; Databricks Blog (2015)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/sql-performance-tuning.html&quot;&gt;Apache Spark SQL Performance Tuning &amp;mdash; Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.databricks.com/en/delta/generated-columns.html&quot;&gt;Delta Lake Generated Columns &amp;mdash; Databricks Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.delta.io/latest/optimizations-oss.html&quot;&gt;Delta Lake OSS &amp;mdash; Optimizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://unraveldata.com/resources/partition-pruning-in-apache-spark/&quot;&gt;Partition Pruning in Apache Spark &amp;mdash; Unravel Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.waitingforcode.com/apache-spark-sql/static-dynamic-partition-pruning-apache-spark/read&quot;&gt;Static and Dynamic Partition Pruning in Apache Spark &amp;mdash; WaitingForCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sparkcodehub.com/spark-catalyst-optimizer&quot;&gt;Spark Catalyst Optimizer &amp;mdash; SparkCodeHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sparkplayground.com/whole-stage-code-gen&quot;&gt;Whole-Stage Code Generation &amp;mdash; SparkPlayground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.databricks.com/blog/2020/05/29/adaptive-query-execution-speeding-up-spark-sql-at-runtime.html&quot;&gt;Adaptive Query Execution &amp;mdash; Databricks Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/748</guid>
      <comments>https://brownbears.tistory.com/748#entry748comment</comments>
      <pubDate>Mon, 27 Apr 2026 02:09:16 +0900</pubDate>
    </item>
    <item>
      <title>[Spark] 파티션 전략과 성능 최적화</title>
      <link>https://brownbears.tistory.com/747</link>
      <description>&lt;h1&gt;파티션 전략 개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Spark에서 파티션(Partition)은 데이터를 분산 처리하는 기본 단위입니다. 파티션의 수와 크기, 분배 방식에 따라 성능이 크게 달라지므로 파티션 전략을 이해하고 올바르게 적용하는 것이 매우 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 파티션이란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2376&quot; data-origin-height=&quot;1670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJlfBw/dJMcaaZm4SO/qFwFokFX0GAuousM2plitk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJlfBw/dJMcaaZm4SO/qFwFokFX0GAuousM2plitk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJlfBw/dJMcaaZm4SO/qFwFokFX0GAuousM2plitk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJlfBw%2FdJMcaaZm4SO%2FqFwFokFX0GAuousM2plitk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2376&quot; height=&quot;1670&quot; data-origin-width=&quot;2376&quot; data-origin-height=&quot;1670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark는 데이터를 여러 파티션으로 나누어 각 Executor의 Task가 하나의 파티션을 담당하는 방식으로 동작합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 수가 &lt;b&gt;너무 적으면&lt;/b&gt; 병렬성이 낮아져 일부 Executor가 유휴 상태가 됩니다.&lt;/li&gt;
&lt;li&gt;파티션 수가 &lt;b&gt;너무 많으면&lt;/b&gt; Task 스케줄링 오버헤드가 증가하여 오히려 성능이 저하됩니다.&lt;/li&gt;
&lt;li&gt;따라서 데이터 크기와 클러스터 자원에 맞는 적정 파티션 수를 유지하는 것이 핵심입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. repartition(100)은 어떻게 데이터를 나누는가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;repartition(100)&lt;/code&gt;과 같이 숫자를 지정하면 데이터를 100개 파티션으로 나눈다는 뜻입니다. 그런데 실제로 어떤 기준으로 각 Row를 파티션에 배정하는지가 핵심입니다. Spark는 크게 두 가지 방식을 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. Round-Robin 방식 &amp;mdash; &lt;code&gt;repartition(n)&lt;/code&gt; 컬럼 미지정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3056&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BLkjX/dJMcaiwjPMN/K5nKicuHa9FVCnrbk1fJA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BLkjX/dJMcaiwjPMN/K5nKicuHa9FVCnrbk1fJA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BLkjX/dJMcaiwjPMN/K5nKicuHa9FVCnrbk1fJA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBLkjX%2FdJMcaiwjPMN%2FK5nKicuHa9FVCnrbk1fJA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3056&quot; height=&quot;1014&quot; data-origin-width=&quot;3056&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬럼을 지정하지 않으면 &lt;b&gt;RoundRobinPartitioning&lt;/b&gt;이 적용됩니다. Row를 순서대로 돌아가며 각 파티션에 하나씩 배분하는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Row1 &amp;rarr; P0,  Row2 &amp;rarr; P1,  Row3 &amp;rarr; P2
Row4 &amp;rarr; P0,  Row5 &amp;rarr; P1,  Row6 &amp;rarr; P2  ...&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;행 수(Row Count) 기준&lt;/b&gt;으로 균등 분배됩니다.&lt;/li&gt;
&lt;li&gt;바이트 크기는 보장하지 않습니다. Row 하나가 1KB인 파티션과 1MB인 파티션이 생길 수 있습니다.&lt;/li&gt;
&lt;li&gt;전체 셔플(Full Shuffle)이 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;df.repartition(100)
# 100개 파티션, 각 파티션에 (전체 행 수 / 100)개의 Row가 배분됨&lt;/code&gt;&lt;/pre&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; &lt;code&gt;repartition(100)&lt;/code&gt;은 데이터를 &lt;b&gt;100등분(행 수 기준)&lt;/b&gt;하는 것이지, 각 파티션이 동일한 바이트 크기를 갖는다는 뜻이 아닙니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. Hash 방식 &amp;mdash; &lt;code&gt;repartition(n, col)&lt;/code&gt; 컬럼 지정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;1742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5hKEI/dJMcajaUPr0/UebG3H6DdOK9uIrkdSTlLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5hKEI/dJMcajaUPr0/UebG3H6DdOK9uIrkdSTlLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5hKEI/dJMcajaUPr0/UebG3H6DdOK9uIrkdSTlLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5hKEI%2FdJMcajaUPr0%2FUebG3H6DdOK9uIrkdSTlLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2358&quot; height=&quot;1742&quot; data-origin-width=&quot;2358&quot; data-origin-height=&quot;1742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬럼을 지정하면 &lt;b&gt;HashPartitioning&lt;/b&gt;이 적용됩니다. 해당 컬럼 값의 해시를 파티션 수로 나눈 나머지(modulo)가 파티션 번호가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;파티션 번호 = MurMur3_hash(컬럼 값) % 파티션 수&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;같은 키 값은 반드시 같은 파티션&lt;/b&gt;에 들어갑니다. 조인&amp;middot;집계 시 셔플 없이 처리하기 위해 사용합니다.&lt;/li&gt;
&lt;li&gt;특정 키에 데이터가 몰리면 파티션 크기가 불균형해집니다(&amp;rarr; 스큐 문제 발생).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;df.repartition(100, &quot;user_id&quot;)
# user_id의 hash % 100 &amp;rarr; 파티션 번호 결정
# 같은 user_id를 가진 Row는 항상 같은 파티션에 모임&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3. 방식 비교 요약&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;repartition(n)&lt;/th&gt;
&lt;th&gt;repartition(n, col)&lt;/th&gt;
&lt;th&gt;coalesce(n)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;내부 방식&lt;/td&gt;
&lt;td&gt;Round-Robin&lt;/td&gt;
&lt;td&gt;Hash (MurMur3)&lt;/td&gt;
&lt;td&gt;파티션 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;분배 기준&lt;/td&gt;
&lt;td&gt;행 수 균등&lt;/td&gt;
&lt;td&gt;키 해시값&lt;/td&gt;
&lt;td&gt;인접 파티션 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;셔플 여부&lt;/td&gt;
&lt;td&gt;전체 셔플&lt;/td&gt;
&lt;td&gt;전체 셔플&lt;/td&gt;
&lt;td&gt;셔플 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;키 동일성 보장&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;O (같은 키 &amp;rarr; 같은 파티션)&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스큐 가능성&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;높음 (키 쏠림 시)&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. repartition vs coalesce&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5twO3/dJMcaaZm4Tn/0KCqPfX5hFAxQUaxrnyD90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5twO3/dJMcaaZm4Tn/0KCqPfX5hFAxQUaxrnyD90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5twO3/dJMcaaZm4Tn/0KCqPfX5hFAxQUaxrnyD90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5twO3%2FdJMcaaZm4Tn%2F0KCqPfX5hFAxQUaxrnyD90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3006&quot; height=&quot;570&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;repartition&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 수만큼 파티션을 새롭게 재분배합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전체 셔플(Full Shuffle)&lt;/b&gt;이 발생하므로 비용이 높습니다.&lt;/li&gt;
&lt;li&gt;파티션 수를 늘리거나 데이터를 균등하게 재분배해야 할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;df.repartition(200)
df.repartition(200, &quot;join_key&quot;)  # 특정 컬럼 기준 해시 분배&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;coalesce&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 수를 줄이는 데 사용합니다.&lt;/li&gt;
&lt;li&gt;셔플 없이 인접 파티션을 병합하므로 비용이 낮습니다.&lt;/li&gt;
&lt;li&gt;단, 데이터 편향(Skew)이 발생할 수 있으므로 주의가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;df.coalesce(50)  # 파티션 수 축소, 셔플 없음&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;권장 방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;파티션 수를 늘려야 할 때&lt;/td&gt;
&lt;td&gt;repartition(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필터링 이후 파티션을 줄일 때&lt;/td&gt;
&lt;td&gt;coalesce(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 키 기준으로 균등 분배해야 할 때&lt;/td&gt;
&lt;td&gt;repartition(n, &quot;key_column&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 파티셔닝(Partitioning) vs 버케팅(Bucketing)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2958&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J2EZK/dJMcaaLRryc/Kpk1mWdE0VX6N0kYlS69H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J2EZK/dJMcaaLRryc/Kpk1mWdE0VX6N0kYlS69H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J2EZK/dJMcaaLRryc/Kpk1mWdE0VX6N0kYlS69H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ2EZK%2FdJMcaaLRryc%2FKpk1mWdE0VX6N0kYlS69H0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2958&quot; height=&quot;642&quot; data-origin-width=&quot;2958&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파티셔닝 (partitionBy)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬럼 값 기준으로 디렉토리를 나눠 데이터를 저장합니다.&lt;/li&gt;
&lt;li&gt;카디널리티가 &lt;b&gt;낮은&lt;/b&gt; 컬럼(예: 날짜, 국가, 상태값)에 적합합니다.&lt;/li&gt;
&lt;li&gt;파티션 프루닝(Partition Pruning)이 가능하여 불필요한 파일 읽기를 방지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;df.write.partitionBy(&quot;year&quot;, &quot;month&quot;).parquet(&quot;path/to/output&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;버케팅 (bucketBy)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 함수로 데이터를 고정 크기 버킷으로 분산하여 저장합니다.&lt;/li&gt;
&lt;li&gt;카디널리티가 &lt;b&gt;높은&lt;/b&gt; 컬럼(예: user_id, order_id)에 적합합니다.&lt;/li&gt;
&lt;li&gt;같은 컬럼과 버킷 수로 버케팅된 테이블 간 조인 시 셔플이 제거됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;df.write.bucketBy(64, &quot;user_id&quot;).sortBy(&quot;user_id&quot;).saveAsTable(&quot;bucketed_table&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 적정 파티션 수와 크기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 파티션 크기:&lt;/b&gt; 파티션 하나의 크기는 &lt;b&gt;128MB ~ 256MB&lt;/b&gt;가 최적입니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 파티션 수 산정 공식
# 방법 1: 데이터 크기 기준
파티션 수 = 총 데이터 크기 / 128MB ~ 256MB

# 방법 2: 클러스터 자원 기준
파티션 수 = Executor 수 &amp;times; Executor당 코어 수 &amp;times; 2~4

# 셔플 파티션 수 설정
spark.conf.set(&quot;spark.sql.shuffle.partitions&quot;, 200)&lt;/code&gt;&lt;/pre&gt;
&lt;aside&gt;ℹ️
&lt;p data-ke-size=&quot;size16&quot;&gt;AQE를 활성화한 경우 초기값을 크게 설정해도 런타임에 자동으로 적절한 값으로 조정됩니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 데이터 스큐(Skew) 문제와 해결 방법&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMB3Z/dJMcabKNtE6/etDnEb23qWoUA7MA0KlTo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMB3Z/dJMcabKNtE6/etDnEb23qWoUA7MA0KlTo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMB3Z/dJMcabKNtE6/etDnEb23qWoUA7MA0KlTo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMB3Z%2FdJMcabKNtE6%2FetDnEb23qWoUA7MA0KlTo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2448&quot; height=&quot;230&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 스큐는 특정 파티션에 데이터가 집중되어 해당 Task만 오래 걸리는 현상입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스큐 진단:&lt;/b&gt; Spark UI &amp;gt; Stages 탭에서 Task 실행 시간 분포를 확인합니다. 최장 Task 시간이 중앙값 대비 5배 이상이면 스큐를 의심합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-1. 솔팅(Salting) 기법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스큐된 키에 임의의 숫자(Salt)를 붙여 여러 파티션에 분산시키는 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;from pyspark.sql.functions import rand, col, lit, concat, explode, array

salt_factor = 10

# 큰 테이블에 salt 추가
df_large = df_large.withColumn(&quot;salt&quot;, (rand() * salt_factor).cast(&quot;int&quot;))
df_large = df_large.withColumn(&quot;salted_key&quot;, concat(col(&quot;join_key&quot;), lit(&quot;_&quot;), col(&quot;salt&quot;)))

# 작은 테이블을 salt_factor배로 복제
salts = array([lit(i) for i in range(salt_factor)])
df_small = df_small.withColumn(&quot;salt&quot;, explode(salts))
df_small = df_small.withColumn(&quot;salted_key&quot;, concat(col(&quot;join_key&quot;), lit(&quot;_&quot;), col(&quot;salt&quot;)))

result = df_large.join(df_small, &quot;salted_key&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6-2. AQE Skew Join 자동 처리 (권장)&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.skewedPartitionFactor&quot;, &quot;5&quot;)
spark.conf.set(&quot;spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes&quot;, &quot;268435456&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 동적 파티션 프루닝(Dynamic Partition Pruning, DPP)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2828&quot; data-origin-height=&quot;1734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dhyf5/dJMcai37I4M/MxIzA2rB2NjkTzYDpnATfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dhyf5/dJMcai37I4M/MxIzA2rB2NjkTzYDpnATfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dhyf5/dJMcai37I4M/MxIzA2rB2NjkTzYDpnATfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDhyf5%2FdJMcai37I4M%2FMxIzA2rB2NjkTzYDpnATfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2828&quot; height=&quot;1734&quot; data-origin-width=&quot;2828&quot; data-origin-height=&quot;1734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DPP란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 파티션 프루닝은 &lt;b&gt;조인 전략이 아니라, 조인 성능을 높이기 위한 최적화 기법&lt;/b&gt;입니다. 작은 차원 테이블의 필터링 결과를 활용하여 큰 팩트 테이블에서 불필요한 파티션을 아예 읽지 않도록 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DPP 적용 조건&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팩트 테이블이 조인 키 컬럼으로 &lt;b&gt;파티셔닝&lt;/b&gt;되어 있어야 합니다.&lt;/li&gt;
&lt;li&gt;차원 테이블 쪽에 선택적인 WHERE 조건이 있어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Equi-Join&lt;/b&gt; (&lt;code&gt;=&lt;/code&gt;) 조건에서만 동작합니다.&lt;/li&gt;
&lt;li&gt;Star Schema 구조에서 가장 효과적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.optimizer.dynamicPartitionPruning.enabled&quot;, &quot;true&quot;)  # 기본 활성화&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Predicate Pushdown&lt;/th&gt;
&lt;th&gt;Dynamic Partition Pruning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;적용 시점&lt;/td&gt;
&lt;td&gt;컴파일 타임&lt;/td&gt;
&lt;td&gt;런타임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;조건 출처&lt;/td&gt;
&lt;td&gt;고정 필터 (WHERE)&lt;/td&gt;
&lt;td&gt;조인 상대 테이블의 필터 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테이블 조건&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;조인 키로 파티셔닝 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합 패턴&lt;/td&gt;
&lt;td&gt;단순 필터 쿼리&lt;/td&gt;
&lt;td&gt;Star Schema 조인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. AQE와 파티션 자동 최적화&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3150&quot; data-origin-height=&quot;1756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EiAJp/dJMcaf7rgTk/jB78TdXHpPov3p6REwlooK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EiAJp/dJMcaf7rgTk/jB78TdXHpPov3p6REwlooK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EiAJp/dJMcaf7rgTk/jB78TdXHpPov3p6REwlooK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEiAJp%2FdJMcaf7rgTk%2FjB78TdXHpPov3p6REwlooK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3150&quot; height=&quot;1756&quot; data-origin-width=&quot;3150&quot; data-origin-height=&quot;1756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark 3.2부터 기본 활성화된 AQE는 런타임 통계를 기반으로 파티션을 자동으로 최적화합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.coalescePartitions.enabled&quot;, &quot;true&quot;)
spark.conf.set(&quot;spark.sql.adaptive.advisoryPartitionSizeInBytes&quot;, &quot;134217728&quot;)  # 128MB
spark.conf.set(&quot;spark.sql.adaptive.coalescePartitions.minPartitionSize&quot;, &quot;1048576&quot;)  # 1MB&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;설정 키&lt;/th&gt;
&lt;th&gt;기본값&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.enabled&lt;/td&gt;
&lt;td&gt;true (3.2+)&lt;/td&gt;
&lt;td&gt;AQE 마스터 스위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.coalescePartitions.enabled&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;셔플 후 파티션 자동 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.advisoryPartitionSizeInBytes&lt;/td&gt;
&lt;td&gt;64MB&lt;/td&gt;
&lt;td&gt;목표 파티션 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.skewJoin.enabled&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;스큐 조인 자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.skewJoin.skewedPartitionFactor&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;스큐 판단 배수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;스큐 판단 최소 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 파티션 최적화 운영 체크리스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설계 단계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 파티셔닝 컬럼은 카디널리티가 낮은 컬럼(날짜, 상태값 등)으로 선택하였습니까?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 조인/집계가 잦은 고카디널리티 컬럼은 버케팅을 고려하였습니까?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Star Schema 구조에서 팩트 테이블을 조인 키로 파티셔닝하여 DPP를 활성화하였습니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 단계&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; AQE가 활성화되어 있습니까? (&lt;code&gt;spark.sql.adaptive.enabled = true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 셔플 파티션 초기값을 충분히 크게 설정하였습니까? (AQE가 런타임에 자동 축소)&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 대용량 필터링 이후 &lt;code&gt;coalesce()&lt;/code&gt;로 파티션 수를 정리하였습니까?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 스큐가 예상되는 조인에서 AQE skewJoin 설정을 확인하였습니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모니터링&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; Spark UI &amp;gt; Stages 탭에서 Task 실행 시간 분포를 확인하고 있습니까?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 최장 Task 시간이 중앙값 대비 5배 이상이라면 스큐를 의심합니다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 파티션 크기가 128MB ~ 256MB 범위에 있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;disabled&quot; type=&quot;checkbox&quot; /&gt; 실행 계획에서 &lt;code&gt;dynamicpruningexpression&lt;/code&gt;으로 DPP 적용 여부를 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 튜닝 기준&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 크기를 128MB ~ 256MB로 유지하면 처리 시간이 &lt;b&gt;20~50% 개선&lt;/b&gt;될 수 있습니다.&lt;/li&gt;
&lt;li&gt;솔팅 기법 사용 시 salt_factor는 스큐 비율에 맞게 조정합니다 (10:1 스큐 &amp;rarr; salt_factor=10).&lt;/li&gt;
&lt;li&gt;동일 컬럼&amp;middot;버킷 수로 버케팅된 테이블 간 조인은 셔플을 제거하여 성능을 크게 향상시킵니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/747</guid>
      <comments>https://brownbears.tistory.com/747#entry747comment</comments>
      <pubDate>Mon, 27 Apr 2026 01:29:17 +0900</pubDate>
    </item>
    <item>
      <title>중앙 메타스토어란</title>
      <link>https://brownbears.tistory.com/746</link>
      <description>&lt;h1&gt;1. 개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 메타스토어(Central Metastore 또는 Data Catalog)는 데이터 레이크&amp;middot;레이크하우스 환경에서 테이블 스키마, 파티션 정보, 위치(Location), 통계, 직렬화 방식(SerDe) 등 &lt;b&gt;데이터의 메타데이터를 통합 관리&lt;/b&gt;하는 핵심 인프라 컴포넌트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark, Trino, Flink, Hive 등 다수의 처리 엔진이 동일한 데이터 자산에 접근할 때, 각 엔진이 개별적으로 스키마 정보를 관리하면 불일치와 중복이 발생합니다. 중앙 메타스토어는 이러한 문제를 해결하기 위해 &lt;b&gt;단일 진실 공급원(Single Source of Truth)&lt;/b&gt; 역할을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 Apache Hive Metastore(HMS)가 사실상 표준으로 자리 잡았으나, 데이터 레이크하우스 시대에 접어들면서 트랜잭션 지원, REST 표준 호환, 멀티 포맷 지원, 세밀한 거버넌스 등을 제공하는 현대적 카탈로그들이 등장하였습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 중앙 메타스토어가 왜 필요한지&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티 엔진 환경의 일관성 확보&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark, Trino, Flink, Hive, Presto 등 다양한 엔진이 S3&amp;middot;GCS&amp;middot;ADLS 같은 공용 오브젝트 스토리지의 동일 데이터를 조회합니다. 중앙 메타스토어가 없으면 각 엔진마다 별도로 테이블 스키마와 파티션 정보를 관리해야 하며, 스키마 변경 시 모든 엔진에 개별 반영해야 하는 운영 부담이 발생합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 거버넌스 및 보안&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블과 컬럼 단위의 접근 권한을 중앙에서 관리하지 않으면 엔진별로 정책이 달라질 위험이 있습니다. 중앙 메타스토어는 RBAC(Role-Based Access Control), 행&amp;middot;열 수준 보안, Tag 기반 정책을 한 곳에서 적용하는 거버넌스 기반을 제공합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 발견성(Discoverability) 향상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수천 개의 테이블이 오브젝트 스토리지에 흩어져 있을 때, 중앙 카탈로그 없이는 필요한 데이터를 찾는 것 자체가 어렵습니다. 중앙 메타스토어는 스키마&amp;middot;통계&amp;middot;태그&amp;middot;Lineage 정보를 통해 데이터 탐색과 이해를 지원합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;레이크하우스 포맷의 트랜잭션 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Iceberg, Delta Lake, Apache Hudi, Apache Paimon 같은 레이크하우스 테이블 포맷은 메타데이터를 통해 ACID 트랜잭션과 Time Travel을 구현합니다. 중앙 메타스토어는 이 메타데이터의 버전 관리와 카탈로그 등록을 책임집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 자동화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 최적화(Compaction), 스냅샷 만료(Snapshot Expiration), 컬럼 통계 수집 등 운영 작업을 카탈로그 레벨에서 자동화할 수 있어, 팀의 운영 부담을 크게 줄입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메타스토어 없이 접근할 때의 문제 &amp;mdash; 데이터 늪(Data Swamp)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타스토어 없이 오브젝트 스토리지 경로를 직접 지정해 데이터에 접근하는 것은 기술적으로 가능합니다. 그러나 규모가 커질수록 &lt;b&gt;데이터 늪(Data Swamp)&lt;/b&gt; 으로 전락할 위험이 높아집니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;# 메타스토어 없이 Parquet 파일 직접 읽기 (물리적 경로 의존)
df = spark.read.format(&quot;parquet&quot;).load(&quot;s3://sales/2020/01/1.parquet&quot;)
df.printSchema()
df.show()

# Delta Lake 테이블 직접 읽기 (경로 기반)
df = spark.read.format(&quot;delta&quot;).load(&quot;s3://sales/&quot;)
df.printSchema()
df.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방식의 핵심 한계:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;물리적 경로 의존&lt;/b&gt;: 논리적 테이블명 없이 S3 경로를 직접 지정하므로, 스토리지 구조 변경 시 모든 파이프라인이 깨집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스키마 변경 추적 불가&lt;/b&gt;: 각 팀&amp;middot;엔진이 독립적으로 메타데이터를 관리하면 스키마 불일치가 발생합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ACID 보장 불가 / 동시성 손상&lt;/b&gt;: 동시 쓰기 시 데이터 손상 위험이 있으며, 트랜잭션 격리를 보장할 수 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;접근 권한&amp;middot;Lineage 관리 불가&lt;/b&gt;: 컬럼&amp;middot;행 수준 보안 정책을 일관되게 적용할 수 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능 최적화 불가&lt;/b&gt;: 파일 프루닝(File Pruning)&amp;middot;파티션 통계는 카탈로그에 등록된 메타데이터 없이는 활용되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 중앙 메타스토어 제품 조사&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hive Metastore (HMS)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;오픈소스&lt;/b&gt; (Apache 2.0)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010년경 Apache Hive 프로젝트의 일부로 시작된 가장 전통적인 메타스토어입니다. 데이터베이스, 테이블, 스키마, 파티션, SerDe 정보를 RDBMS(MySQL, PostgreSQL 등) 백엔드에 저장하며, Thrift 서비스를 통해 다중 엔진이 동일한 메타데이터에 접근합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 성숙하고 광범위하게 채택된 표준으로, 거의 모든 빅데이터 엔진과 호환됩니다.&lt;/li&gt;
&lt;li&gt;단일 테이블 단위 트랜잭션만 지원하며, 멀티 테이블 원자적 변경은 불가능합니다.&lt;/li&gt;
&lt;li&gt;Thrift 기반 비공식 표준으로, REST가 아니어서 클라우드 네이티브 환경에 어색합니다.&lt;/li&gt;
&lt;li&gt;파티션 메타데이터가 많아질 경우 RDBMS 병목이 발생합니다.&lt;/li&gt;
&lt;li&gt;2026년 기준 업계 컨센서스는 신규 프로젝트에서는 HMS 대신 REST Catalog로 시작하라는 방향입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;매니지드 서비스 옵션&lt;/b&gt;: AWS Glue Data Catalog, GCP Dataproc Metastore, Cloudera CDP, EMR/HDInsight/Dataproc 내장&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Glue Data Catalog&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;클라우드 매니지드&lt;/b&gt; (AWS, Closed Source)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS의 완전 매니지드 통합 메타데이터 저장소로, S3&amp;middot;RDS&amp;middot;Redshift&amp;middot;Athena 전반에 걸쳐 테이블 정의와 스키마를 관리합니다. 서버리스 구조로 자동 스키마 디스커버리(Crawlers), Schema Registry, Iceberg 테이블 자동 최적화(Compaction&amp;middot;Snapshot Expiration&amp;middot;컬럼 통계)를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 생태계(Athena, EMR, Redshift, SageMaker, Lake Formation) 전체에서 사실상 표준 카탈로그입니다.&lt;/li&gt;
&lt;li&gt;Iceberg 네이티브 자동 최적화는 타 매니지드 카탈로그 대비 차별화된 기능입니다.&lt;/li&gt;
&lt;li&gt;월 100만 객체&amp;middot;요청까지 무료 티어를 제공합니다.&lt;/li&gt;
&lt;li&gt;AWS 종속(Vendor Lock-in)이 강하며, Iceberg REST 표준 미준수로 외부 엔진은 어댑터가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가격&lt;/b&gt;: 객체 수($1/100만 개) + 요청 수($1/100만 건) + 최적화 작업($0.44/DPU-시간)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Google Cloud Dataproc Metastore&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;클라우드 매니지드&lt;/b&gt; (GCP, 백엔드는 OSS HMS)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP의 완전 매니지드 Apache Hive Metastore 서비스입니다. HMS Thrift API를 100% 호환하여 기존 Hive 기반 워크로드를 그대로 이전할 수 있습니다. 2024년 이후 GCP는 Iceberg 중심 신규 워크로드를 위해 BigLake Metastore(Iceberg REST 카탈로그 규격 준수)를 별도 출시하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HMS 100% 호환으로 Hadoop/Hive 생태계 마이그레이션 부담이 최소화됩니다.&lt;/li&gt;
&lt;li&gt;Zonal HA 기본 제공, Regional HA&amp;middot;DR 옵션으로 엔터프라이즈 SLA를 충족합니다.&lt;/li&gt;
&lt;li&gt;항상 켜진 인스턴스 기반 과금으로 소규모&amp;middot;간헐적 워크로드에서 비용이 높습니다.&lt;/li&gt;
&lt;li&gt;Iceberg 신규 워크로드는 BigLake Metastore와 병행 운영이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가격&lt;/b&gt;: Enterprise 기준 Scaling Factor &amp;times; $3.42/시간 (최소 프로덕션 권장 시 월 약 $2,462)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Delta Lake Unity Catalog &amp;mdash; 상용 버전 (Databricks)&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;상용 매니지드&lt;/b&gt; (Databricks SaaS)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Databricks가 2022년 GA한 통합 거버넌스 솔루션으로, 3-Level Namespace(catalog.schema.table)를 도입하여 기존 Hive의 2-Level 한계를 해결하였습니다. HMS와 Apache Ranger의 기능을 Databricks 플랫폼 내에서 통합한 형태로, 단순 메타스토어를 넘어 AI&amp;middot;ML 자산까지 거버넌스 범위를 확장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ANSI SQL 기반 GRANT/REVOKE, ABAC, 행&amp;middot;열 수준 보안(Row Filter&amp;middot;Column Mask)을 제공합니다.&lt;/li&gt;
&lt;li&gt;컬럼 단위 Lineage 자동 추적, Audit 로그(System Tables), Delta Sharing 통합이 포함됩니다.&lt;/li&gt;
&lt;li&gt;MySQL&amp;middot;PostgreSQL&amp;middot;Snowflake&amp;middot;BigQuery&amp;middot;Redshift 등을 Lakehouse Federation으로 가상 마운트합니다.&lt;/li&gt;
&lt;li&gt;Databricks 워크스페이스 종속이 강하며, Apache Ranger와의 공식 연동은 없습니다(대체 포지셔닝).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Delta Lake Unity Catalog &amp;mdash; 오픈소스 버전&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;오픈소스&lt;/b&gt; (Apache 2.0, LF AI &amp;amp; Data Foundation)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년 6월 Databricks가 Apache 2.0 라이선스로 공개하고 LF AI &amp;amp; Data Foundation에 기증한 버전입니다. OpenAPI 기반 REST API, Iceberg REST Catalog API 호환, 멀티 포맷&amp;middot;멀티 엔진 지향 설계가 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상용 vs 오픈소스 주요 차이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영역 OSS 상용 (Databricks)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;인증/SSO&lt;/td&gt;
&lt;td&gt;기본 Bearer 토큰&lt;/td&gt;
&lt;td&gt;OIDC/SAML SSO, SCIM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권한 모델&lt;/td&gt;
&lt;td&gt;기본 GRANT/REVOKE&lt;/td&gt;
&lt;td&gt;ABAC, Row Filter, Column Mask&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lineage&lt;/td&gt;
&lt;td&gt;미지원 (로드맵)&lt;/td&gt;
&lt;td&gt;컬럼 단위 자동 캡처&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Log&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;System Tables 자동 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lakehouse Federation&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;MySQL/Snowflake/BigQuery 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동 최적화&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;Predictive Optimization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI/ML 자산&lt;/td&gt;
&lt;td&gt;추상화만&lt;/td&gt;
&lt;td&gt;MLflow&amp;middot;Feature Store 완전 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Sharing&lt;/td&gt;
&lt;td&gt;별도 서버 필요&lt;/td&gt;
&lt;td&gt;네이티브 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OSS는 &lt;b&gt;&quot;카탈로그 API + 멀티 포맷 추상화&quot;&lt;/b&gt; 가 핵심이며, 거버넌스 심화 기능(ABAC, Lineage, Audit, Federation)은 상용 버전의 차별점으로 남아 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Tabular&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;상용 매니지드&lt;/b&gt; (2024년 6월 Databricks 인수, 신규 채택 비권장)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Iceberg 공동 창시자 Ryan Blue가 2021년 설립한 Iceberg 전용 매니지드 카탈로그&amp;middot;자동 최적화 서비스입니다. 2024년 6월 Databricks가 인수하여 신규 고객 온보딩이 중단되었으며, 기존 고객은 Unity Catalog로 이전이 안내됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 가치 (인수 이전)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iceberg Auto-compaction&amp;middot;Auto-clustering&amp;middot;Snapshot Expiration 등 자동화&lt;/li&gt;
&lt;li&gt;Iceberg REST Catalog 사양의 레퍼런스 구현 수준&lt;/li&gt;
&lt;li&gt;멀티 엔진(Spark, Trino, Flink, Snowflake, Athena) 동시 접근 안정성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Project Nessie&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;오픈소스&lt;/b&gt; (Apache 2.0, Dremio 주도) / &lt;b&gt;매니지드&lt;/b&gt;: Dremio Arctic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dremio가 주도하여 시작한 트랜잭셔널 카탈로그로, &lt;b&gt;Git-like 의미론(branch, tag, commit, merge)을 데이터 레이크에 적용&lt;/b&gt;합니다. 카탈로그 메타데이터를 버전 관리되는 데이터 구조에 저장하여 격리된 dev/test 환경, 데이터 실험, 롤백, Zero-copy Clone을 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Multi-table Atomic Commit을 지원하여 여러 테이블에 걸친 트랜잭션 보장이 가능합니다.&lt;/li&gt;
&lt;li&gt;Iceberg 외 포맷(Delta, Hudi, Paimon) 지원이 약해 실질적으로 Iceberg 전용에 가깝습니다.&lt;/li&gt;
&lt;li&gt;메타데이터 백엔드로 RocksDB, MongoDB, DynamoDB, PostgreSQL 등 다양한 옵션을 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;매니지드 서비스&lt;/b&gt;: Dremio Arctic (자동 Compaction, Garbage Collection 등 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apache Polaris&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;오픈소스&lt;/b&gt; (Apache 2.0 Incubating, Snowflake 기증) / &lt;b&gt;매니지드&lt;/b&gt;: Snowflake Open Catalog&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년 7월 Snowflake가 오픈소스화하고 ASF에 기증한 &lt;b&gt;Apache Iceberg REST Catalog 표준의 레퍼런스 구현체&lt;/b&gt;입니다. Vendor-neutral 멀티 엔진 상호운용성에 초점을 맞추며, Credential Vending(엔진별 임시 자격증명 발급)과 Fine-grained RBAC을 기본으로 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iceberg REST 표준의 레퍼런스 구현으로 미래 표준에 가장 잘 정렬되어 있습니다.&lt;/li&gt;
&lt;li&gt;Snowflake, Databricks, Dremio, AWS, GCP 어디에도 락인되지 않는 벤더 중립성을 표방합니다.&lt;/li&gt;
&lt;li&gt;Delta Lake와 Hudi 지원이 추가되며 Unified Catalog로 확장 중입니다.&lt;/li&gt;
&lt;li&gt;2024년 오픈소스화로 성숙도 한계가 있으며, Git-like Branching 같은 고급 기능은 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Snowflake Open Catalog&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;클라우드 매니지드&lt;/b&gt; (백엔드는 OSS Apache Polaris)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snowflake가 제공하는 Apache Polaris 기반 매니지드 카탈로그 서비스로, 2024년 10월 GA되었습니다. Internal Catalog(직접 관리, R/W)와 External Catalog(Snowflake&amp;middot;Glue 등에서 동기화, R/O) 두 모드를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iceberg REST 표준 100% 준수로 진정한 멀티 엔진 상호운용성을 제공합니다.&lt;/li&gt;
&lt;li&gt;오픈소스(Apache Polaris) 기반으로 자체 호스팅도 가능합니다.&lt;/li&gt;
&lt;li&gt;Snowflake 워크로드와 외부 OSS 엔진 간 단일 카탈로그 운영이 가능합니다.&lt;/li&gt;
&lt;li&gt;2026년 상반기부터 REST API 요청 단위 빌링 시작 예정으로 TCO 예측이 불확실합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apache Gravitino&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형: &lt;b&gt;오픈소스&lt;/b&gt; (Apache 2.0, Datastrato 기증 &amp;rarr; ASF TLP) / 매니지드 서비스: 없음 (자체 호스팅만)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년 Datastrato가 개발하여 2024년 6월 Apache Incubator에 기증, &lt;b&gt;2025년 6월 Apache Top-Level Project(TLP)로 정식 졸업&lt;/b&gt;한 AI-native 유니버설 메타스토어입니다. &quot;카탈로그의 카탈로그(catalog of catalogs)&quot;를 표방하며, 테이블&amp;middot;파일셋&amp;middot;ML 모델&amp;middot;벡터&amp;middot;메시징 토픽까지 통합 거버넌스를 제공합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: LinkedIn의 오픈소스 프로젝트는 별도의 &lt;b&gt;OpenHouse&lt;/b&gt;(Iceberg 테이블 RESTful 프로비저닝)이며, Gravitino는 Datastrato가 기증한 별개 프로젝트입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 아키텍처: Metalake &amp;rarr; Catalog &amp;rarr; Schema &amp;rarr; Table/Fileset/Model&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Metalake&lt;/b&gt;: 최상위 컨테이너/테넌트 (조직 단위)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Catalog&lt;/b&gt;: 특정 메타데이터 소스(Hive, Iceberg, MySQL 등)와 연결되는 Connector 묶음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Schema / Table / Fileset / Model / Topic&lt;/b&gt;: 테이블뿐 아니라 ML 모델, 파일셋, 메시징 토픽까지 1급 객체로 모델링&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 복제&amp;middot;동기화하지 않고 원본 시스템에서 &lt;b&gt;직접 관리(direct management)&lt;/b&gt; 하는 방식으로 sync 지연&amp;middot;불일치가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iceberg / Delta Lake(1.2+) / Hudi / Paimon을 모두 Generic Lakehouse Catalog로 지원합니다.&lt;/li&gt;
&lt;li&gt;Trino, Spark, Flink, Apache Doris, ClickHouse 등 폭넓은 MPP 엔진을 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ML 모델(Model Catalog), 파일셋(Fileset), Lance 벡터 임베딩, MCP Server&lt;/b&gt;를 1급 시민으로 통합하는 AI-native 설계입니다.&lt;/li&gt;
&lt;li&gt;HMS, JDBC 기반 RDB(MySQL, PostgreSQL, ClickHouse 등), Kafka 토픽을 Federation으로 직접 관리합니다.&lt;/li&gt;
&lt;li&gt;2026년 3월 기준 최신 버전은 1.2.0이며, Uber&amp;middot;Pinterest&amp;middot;Apple&amp;middot;Cloudflare 등에서 프로덕션 운영 중입니다.&lt;/li&gt;
&lt;li&gt;매니지드 SaaS 서비스는 없으며, Kubernetes/EC2 기반 자체 호스팅이 기본 운영 모델입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 제품들과 Data Lakehouse 포맷과의 조합&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 Apache Iceberg Delta Lake Apache Hudi Apache Paimon&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HiveCatalog 지원 (단일 테이블 한계)&lt;/td&gt;
&lt;td&gt;Spark Delta Sync 경유 (제한적)&lt;/td&gt;
&lt;td&gt;HiveSync 지원&lt;/td&gt;
&lt;td&gt;HiveCatalog 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;네이티브 + 자동 최적화&lt;/td&gt;
&lt;td&gt;네이티브&lt;/td&gt;
&lt;td&gt;네이티브&lt;/td&gt;
&lt;td&gt;공식 미지원 (Flink 경유)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GCP Dataproc Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spark/Trino 경유 (BigLake 권장)&lt;/td&gt;
&lt;td&gt;Spark 경유 지원&lt;/td&gt;
&lt;td&gt;Dataproc 1.3+ 지원&lt;/td&gt;
&lt;td&gt;Flink 커넥터 경유 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC 상용 (Databricks)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;UniForm + REST API&lt;/td&gt;
&lt;td&gt;1급 시민 (최우선)&lt;/td&gt;
&lt;td&gt;External Table만&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC OSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;REST API 네이티브&lt;/td&gt;
&lt;td&gt;네이티브&lt;/td&gt;
&lt;td&gt;External Table&lt;/td&gt;
&lt;td&gt;약함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Tabular&lt;/b&gt; (단종)&lt;/td&gt;
&lt;td&gt;1급 시민 (유일)&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1급 시민 (핵심)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;약함/비공식&lt;/td&gt;
&lt;td&gt;약함/비공식&lt;/td&gt;
&lt;td&gt;약함/비공식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1급 시민 (레퍼런스)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Generic Table API 추가 지원&lt;/td&gt;
&lt;td&gt;로드맵/제한적&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Snowflake Open Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1급 시민&lt;/td&gt;
&lt;td&gt;Polaris Generic Table 경유&lt;/td&gt;
&lt;td&gt;로드맵&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1급 시민 (REST)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Generic Lakehouse Catalog 지원 (1.2+)&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1급 시민&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;포맷별 최적 조합 요약&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Iceberg 중심&lt;/b&gt; &amp;rarr; Apache Polaris / Snowflake Open Catalog / Project Nessie / AWS Glue / Apache Gravitino&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Delta Lake 중심&lt;/b&gt; &amp;rarr; UC 상용 (Databricks) / UC OSS / Apache Gravitino&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hudi 중심&lt;/b&gt; &amp;rarr; HMS / AWS Glue / GCP Dataproc Metastore / Apache Gravitino&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Paimon 중심&lt;/b&gt; &amp;rarr; HMS (Flink + HiveCatalog) / AWS Glue (Flink 경유) / Apache Gravitino (1급 시민)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멀티 포맷&lt;/b&gt; &amp;rarr; HMS (넓지만 낡음) / UC OSS (Iceberg&amp;middot;Delta 지원) / &lt;b&gt;Apache Gravitino (4대 포맷 통합)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Apache Paimon 자체 카탈로그 옵션&lt;/b&gt;: Paimon은 외부 중앙 메타스토어에 의존하지 않는 경량 카탈로그를 자체적으로 제공합니다. &lt;b&gt;Filesystem Catalog&lt;/b&gt;(로컬&amp;middot;S3&amp;middot;HDFS 경로 기반, 운영 부담 최소), &lt;b&gt;JDBC Catalog&lt;/b&gt;(MySQL&amp;middot;PostgreSQL 기반, 팀 공유), &lt;b&gt;REST Catalog&lt;/b&gt;(HTTP 기반, 분산 환경) 세 가지 옵션이 있어, 소규모 환경이나 Flink 전용 파이프라인에서 HMS 없이 독립 운영이 가능합니다. HMS 운영 부담을 줄이고 싶은 Paimon 중심 팀에게 유효한 선택지입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 제품들과 MPP 엔진, 클라우드 서비스, DB와의 조합&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MPP 엔진 호환성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 Spark Trino / Presto Flink Hive Doris / StarRocks&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ 완전&lt;/td&gt;
&lt;td&gt;✅ 완전&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ 네이티브&lt;/td&gt;
&lt;td&gt;✅ (대부분)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ (EMR)&lt;/td&gt;
&lt;td&gt;✅ Athena (Trino 기반)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (EMR Hive)&lt;/td&gt;
&lt;td&gt;부분 (어댑터 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GCP Dataproc Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;간접&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC 상용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ (DBR 최적화)&lt;/td&gt;
&lt;td&gt;✅ (커넥터)&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;간접&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC OSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ (Spark 3.5+)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;Iceberg REST 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;Dremio 경유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;✅ Doris, StarRocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Snowflake Open Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;✅ (REST 호환 엔진)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;✅ Doris, ClickHouse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라우드 서비스 통합&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 AWS EMR GCP Dataproc Azure HDInsight Snowflake BigQuery&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ 기본&lt;/td&gt;
&lt;td&gt;✅ (Dataproc Metastore)&lt;/td&gt;
&lt;td&gt;✅ 기본&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;1급&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;External Catalog&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GCP Dataproc Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;1급&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;BigLake 연계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC 상용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;간접 (OSS 커넥터)&lt;/td&gt;
&lt;td&gt;간접&lt;/td&gt;
&lt;td&gt;간접&lt;/td&gt;
&lt;td&gt;Federation&lt;/td&gt;
&lt;td&gt;Federation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC OSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ (직접 배포 가능)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Iceberg REST&lt;/td&gt;
&lt;td&gt;Iceberg REST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;사용자 구성 (REST)&lt;/td&gt;
&lt;td&gt;사용자 구성 (REST)&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;✅ Open Catalog&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Snowflake Open Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;✅ (REST 연결)&lt;/td&gt;
&lt;td&gt;✅ (REST 연결)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;네이티브&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;사용자 구성&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RDB / 외부 DB 페더레이션&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UC 상용&lt;/b&gt;: MySQL, PostgreSQL, Snowflake, BigQuery, Redshift, SQL Server, Salesforce, Oracle, Teradata 페더레이션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AWS Glue&lt;/b&gt;: Lake Formation 통해 RDS&amp;middot;Redshift&amp;middot;Aurora 메타데이터 통합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GCP Dataproc Metastore&lt;/b&gt;: BigQuery와 메타데이터 노출 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Polaris / Snowflake Open Catalog&lt;/b&gt;: Federation 로드맵 (Unity Catalog, Glue, HMS에서 점진적 통합 예정)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;: HMS, MySQL, PostgreSQL, ClickHouse, Kafka 등을 Connector로 직접 Federation. Snowflake는 JDBC Catalog 또는 Iceberg REST 방식으로 연동 가능 (&amp;rarr; 9번 아키텍처 패턴 참고)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HMS / Nessie / UC OSS&lt;/b&gt;: 외부 DB 페더레이션 기능 없음 (엔진 레벨에서 처리)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 거버넌스 도구 통합&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙 메타스토어는 메타데이터 관리의 기술 계층을 담당하지만, 엔터프라이즈 데이터 거버넌스는 인가(Authorization), 데이터 계보(Lineage), 데이터 발견(Discovery)&amp;middot;분류(Classification)를 별도 레이어에서 처리하는 경우가 많습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apache Ranger &amp;mdash; 정책 기반 인가 엔진&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Ranger는 Hadoop 생태계에서 출발한 오픈소스 인가 프레임워크로, 중앙화된 정책 관리와 감사 로깅을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타스토어 통합 방식 성숙도&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS/Hive&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RangerHiveAuthorizerFactory 플러그인으로 네이티브 통합. 테이블&amp;middot;켼럼&amp;middot;태그 기반 정책&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Production&lt;/b&gt; (CDP/EMR 표준)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 플러그인 없음. EMR + Ranger 조합으로 콤퓨트 레벨 인가. Glue 자체는 Lake Formation 사용&lt;/td&gt;
&lt;td&gt;&lt;b&gt;간접 지원&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Databricks UC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;네이티브 미지원. Privacera(Ranger 상용 fork)가 UC 정책을 Ranger UX로 매핑&lt;/td&gt;
&lt;td&gt;&lt;b&gt;써드파티 우회&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RANGER-4910에서 플러그인 개발 요청 중. 2026년 4월 기준 미출시. Polaris 자체 RBAC 사용&lt;/td&gt;
&lt;td&gt;&lt;b&gt;로드맵 / 부재&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Gravitino 자체 RBAC으로 인가. Ranger 직접 통합 없음&lt;/td&gt;
&lt;td&gt;&lt;b&gt;부재&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Ranger가 여전히 필요한 케이스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HDFS, HBase, Kafka, Solr 등 레거시 Hadoop 자산 비중이 높은 환경&lt;/li&gt;
&lt;li&gt;온프레미스&amp;middot;엠어개플(air-gapped) 데이터 플랫폼 (CDP, MapR-legacy)&lt;/li&gt;
&lt;li&gt;다중 엔진(Trino + Hive + HBase)에 동일 Tag 기반 정책(TBAC) 적용이 필요한 경우&lt;/li&gt;
&lt;li&gt;컴플라이언스 요건으로 OSS 자체 호스팅이 필수인 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apache Atlas &amp;mdash; 데이터 계보 및 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Atlas는 JanusGraph + Solr 기반 메타데이터&amp;middot;거버넌스 프레임워크로, Lineage 자동 캐포와 태그 기반 분류(Classification)를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 통합 방식&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS/Hive&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HiveHook 네이티브 통합. 가장 성숙. 테이블&amp;middot;켼럼 레벨 Lineage 자동 캐포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Glue 메타데이터를 Atlas로 import하는 패턴 (자동 sync 아님 import 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC / Polaris / Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;네이티브 미지원. UC는 자체 Lineage 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 기준 Atlas는 Hadoop/CDP 레거시 환경에 최적화되어 있으며, 클라우드 네이티브 카탈로그와의 통합이 약합니다. &lt;b&gt;신규 환경에서의 Atlas 도입은 권장하지 않으며&lt;/b&gt;, OpenMetadata나 DataHub 같은 모던 거버넌스 카탈로그가 대안입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;OpenMetadata &amp;mdash; 모던 오픈소스 거버넌스 카탈로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenMetadata는 120개 이상의 커넥터를 제공하는 모던 오픈소스 메타데이터 플랫폼으로, Ranger+Atlas 조합을 대체하는 단일 거버넌스 레이어를 목표로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 커넥터 상태&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS/Hive&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GA, 성숙. 메타데이터 + Lineage + Profiling 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GA. 메타데이터&amp;middot;Lineage&amp;middot;파티션 정보 수집&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Databricks UC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GA (unity-catalog 커넥터). UC Lineage 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Polaris&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg REST 커넥터로 우회. 전용 커넥터는 미출시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg 커넥터 + Nessie REST로 Branch 메타데이터 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 커넥터 없음. HMS/Iceberg 경로로 우회&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;거버넌스 아키텍처 권장 패턴 &amp;mdash; Two-Layer Architecture&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업계는 &lt;b&gt;Two-Layer Architecture&lt;/b&gt;로 수렴하고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기술 카탈로그(Technical Catalog)&lt;/b&gt;: Polaris, UC, Glue, HMS, Nessie, Gravitino &amp;mdash; Iceberg/Delta REST 스펙 구현, 엔진 인가의 source of truth&lt;/li&gt;
&lt;li&gt;&lt;b&gt;거버넌스 카탈로그(Governance Catalog)&lt;/b&gt;: OpenMetadata, DataHub, Atlan, Collibra &amp;mdash; 비즈니스 메타데이터, Lineage, Discovery, Stewardship&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 가이드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 권장 조합&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greenfield (신규 2026)&lt;/td&gt;
&lt;td&gt;카탈로그 네이티브 RBAC (UC/Lake Formation/Polaris) + OpenMetadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brownfield (Hadoop 레거시)&lt;/td&gt;
&lt;td&gt;Ranger + Atlas 유지. 클라우드 마이그레이션 시 Privacera로 정책 연속성 확보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;하이브리드 멀티 클라우드&lt;/td&gt;
&lt;td&gt;카탈로그 네이티브가 Enforcement, OpenMetadata/Collibra가 Governance Layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. TCO 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;카탈로그별 비용 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 소형 (월) 중형 (월) 대형 (월) 과금 모델&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;HMS (자체)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~$200 + 0.1 FTE&lt;/td&gt;
&lt;td&gt;~$780 + 0.3 FTE&lt;/td&gt;
&lt;td&gt;~$3,250 + 0.7 FTE&lt;/td&gt;
&lt;td&gt;인프라 + 운영 인력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~$4&lt;/td&gt;
&lt;td&gt;~$140&lt;/td&gt;
&lt;td&gt;~$2,500&lt;/td&gt;
&lt;td&gt;객체 수 + 요청 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GCP DPMS Enterprise&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;$2,500 (항상 켜집)&lt;/td&gt;
&lt;td&gt;$2,500&lt;/td&gt;
&lt;td&gt;$5,000&lt;/td&gt;
&lt;td&gt;인스턴스 시간 (고정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;UC 상용 (Databricks)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;+$750 추가&lt;/td&gt;
&lt;td&gt;+$7,500 추가&lt;/td&gt;
&lt;td&gt;+$75,000 추가&lt;/td&gt;
&lt;td&gt;DBU 프리미엄 차액&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Polaris / Open Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;$0 &amp;rarr; ~$50 (H1 과금 후)&lt;/td&gt;
&lt;td&gt;$0 &amp;rarr; ~$200&lt;/td&gt;
&lt;td&gt;$0 &amp;rarr; ~$2,000&lt;/td&gt;
&lt;td&gt;API 요청 (2026 H1~)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Project Nessie OSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~$200&lt;/td&gt;
&lt;td&gt;~$700&lt;/td&gt;
&lt;td&gt;~$2,500&lt;/td&gt;
&lt;td&gt;인프라&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Gravitino OSS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;~$200&lt;/td&gt;
&lt;td&gt;~$700&lt;/td&gt;
&lt;td&gt;~$2,500&lt;/td&gt;
&lt;td&gt;인프라&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;소규모 워크로드&lt;/b&gt;: AWS Glue(~$4/월) 또는 Polaris(현재 무료)가 압도적으로 저렴합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GCP Dataproc Metastore&lt;/b&gt;는 워크로드가 없어도 항상 과금되므로 소형 환경에는 비효율적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UC 상용&lt;/b&gt;은 Databricks를 이미 사용 중인 환경에서는 증분 비용(marginal cost)이지만, 비-Databricks 환경에서는 매우 비싘니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;숨겨진 비용 요소&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영 인력 (가장 큰 숨겨진 비용)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체 운영(HMS&amp;middot;Polaris OSS&amp;middot;Nessie&amp;middot;Gravitino)은 시니어 데이터 엔지니어 기준 대형 환경에서 연 $75K&amp;ndash;$150K의 운영 인건비가 발생합니다. 매니지드 서비스(Glue&amp;middot;DPMS&amp;middot;UC)는 0.05&amp;ndash;0.3 FTE 수준으로 감소합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마이그레이션 비용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 예상 공수&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HMS &amp;rarr; Glue / DPMS&lt;/td&gt;
&lt;td&gt;메타데이터 export/import + 파티션 재등록. 중형 1&amp;ndash;3 person-month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMS &amp;rarr; Unity Catalog&lt;/td&gt;
&lt;td&gt;External table 변환 + UC catalog 등록 + 권한 재정의. 1&amp;ndash;6 person-month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMS &amp;rarr; Iceberg REST (Polaris/Nessie)&lt;/td&gt;
&lt;td&gt;Hive&amp;rarr;Iceberg 테이블 변환(스냅샷/리라이트) + 카탈로그 등록. 테이블당 컴퓨트 비용 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ranger &amp;rarr; UC 거버넌스&lt;/td&gt;
&lt;td&gt;정책 자동 변환 도구 부재로 수작업 변환 + 검증 필요. 6&amp;ndash;12 person-month (대규모)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lock-in 탈출 비용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UC 상용 &amp;rarr; 타 카탈로그&lt;/b&gt;: Managed Table을 External/Iceberg로 변환, 정책 재구성 필요. 사실상 재마이그레이션 수준&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Glue &amp;rarr; 타 카탈로그&lt;/b&gt;: Iceberg 테이블이면 거의 무비용. Hive 테이블은 중간 수준&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OSS 카탈로그(Polaris&amp;middot;Nessie&amp;middot;HMS&amp;middot;UC OSS&amp;middot;Gravitino)&lt;/b&gt;: 락인 거의 없음. OSS의 핵심 TCO 장점&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Snowflake Open Catalog 과금 주의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 H1부터 과금이 시작될 예정입니다. 현재 무료 기간에 마이그레이션을 완료하면 전환 비용 없이 진입 장벽을 낙었습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 마이그레이션 전략&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HMS에서 현대적 카탈로그로의 이전&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레거시 Hive Metastore에서 현대적 카탈로그로 이전할 때는 &lt;b&gt;점진적 접근(Incremental Migration)&lt;/b&gt; 이 권장됩니다. Big Bang 방식은 롤백이 어렵고 운영 중단 위험이 높습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단계별 마이그레이션 로드맵&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 1 &amp;mdash; 인벤토리 및 의존성 분석 (1&amp;ndash;2개월)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 테이블 목록, 파티션 수, 접근 엔진, 권한 정책 문서화&lt;/li&gt;
&lt;li&gt;비즈니스 크리티컈 테이블과 배치 파이프라인 식별&lt;/li&gt;
&lt;li&gt;대상 카탈로그 선택 및 POC 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 2 &amp;mdash; Iceberg 포맷 변환 (테이블당)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신규 테이블: 처음부터 Iceberg 포맷으로 작성&lt;/li&gt;
&lt;li&gt;기존 Hive 테이블: ALTER TABLE ... SET TBLPROPERTIES ('table_type'='ICEBERG')로 인플레이스 마이그레이션 또는 CALL system.rewrite_data_files()로 전체 재작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 3 &amp;mdash; 카탈로그 등록 및 이중 운영&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새 카탈로그(Polaris&amp;middot;Nessie&amp;middot;Glue 등)에 변환된 테이블 등록&lt;/li&gt;
&lt;li&gt;HMS와 새 카탈로그를 &lt;b&gt;병행 운영(dual-write/dual-read)&lt;/b&gt; 기간 유지&lt;/li&gt;
&lt;li&gt;엔진별 카탈로그 설정 변경 (Spark spark.sql.catalog.*, Trino catalog.properties)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 4 &amp;mdash; 트래픽 이전 및 HMS 폐기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀별&amp;middot;도메인별 순차 이전&lt;/li&gt;
&lt;li&gt;HMS 메타데이터 아카이브 보관 후 폐기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클라우드별 권장 마이그레이션 경로&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 환경 대상 카탈로그 주요 도구/서비스&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HMS + AWS EMR&lt;/td&gt;
&lt;td&gt;AWS Glue Data Catalog&lt;/td&gt;
&lt;td&gt;AWS Glue Crawler, EMR Iceberg 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMS + GCP Dataproc&lt;/td&gt;
&lt;td&gt;GCP Dataproc Metastore &amp;rarr; BigLake Metastore&lt;/td&gt;
&lt;td&gt;Dataproc Metastore import, BigLake REST Catalog API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMS + Databricks&lt;/td&gt;
&lt;td&gt;Databricks Unity Catalog&lt;/td&gt;
&lt;td&gt;Databricks UPGRADE TABLE 명령, HMS Federation &amp;rarr; UC External Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMS + 멀티 클라우드&lt;/td&gt;
&lt;td&gt;Apache Polaris / Nessie (자체 호스팅)&lt;/td&gt;
&lt;td&gt;PyIceberg migration tool, Iceberg REST 카탈로그 마이그레이션 스크립트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마이그레이션 주요 함정(Pitfall)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;파티션 수 과다&lt;/b&gt;: HMS에서 수억 개 파티션이 있는 테이블은 Iceberg 변환 시 메타데이터 재생성 비용이 큰 점을 쿠사해야 합니다. 먼저 파티셔닝 전략을 재검토하십시오.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;직렬화 포맷(SerDe) 불일치&lt;/b&gt;: 특수 SerDe를 사용하는 테이블은 Iceberg 변환 전 Parquet/ORC 재작성이 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권한 정책 공백&lt;/b&gt;: Ranger 정책을 새 카탈로그의 RBAC 모델로 1:1 매핑하는 자동화 도구가 없으므로, 수작업 변환 및 검증 기간을 충분히 확보하십시오.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;엔진별 동작 차이&lt;/b&gt;: HMS와 Iceberg 카탈로그는 파티션 프루닝, 통계 활용 방식이 다를 수 있어 쿼리 성능 회귀(regression) 검증이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 아키텍처 패턴&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단일 카탈로그 vs 멀티 카탈로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 단일 카탈로그 멀티 카탈로그&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;운영 복잡도&lt;/td&gt;
&lt;td&gt;낙음&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;벤더 종속 위험&lt;/td&gt;
&lt;td&gt;높음 (단일 벤더 의존)&lt;/td&gt;
&lt;td&gt;낙음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;멀티 클라우드 지원&lt;/td&gt;
&lt;td&gt;어려움&lt;/td&gt;
&lt;td&gt;자연스러움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;엔진 다양성&lt;/td&gt;
&lt;td&gt;제한&lt;/td&gt;
&lt;td&gt;유연&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;거버넌스 일관성&lt;/td&gt;
&lt;td&gt;강함&lt;/td&gt;
&lt;td&gt;별도 Federation Layer 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 상황&lt;/td&gt;
&lt;td&gt;단일 클라우드&amp;middot;단일 팀&lt;/td&gt;
&lt;td&gt;멀티 클라우드&amp;middot;대형 조직&amp;middot;Data Mesh&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Two-Layer Architecture&lt;/h2&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;┌──────────────────────────────────────────────────┐
│    거버넌스 카탈로그 (Governance Layer)       │
│  OpenMetadata / DataHub / Collibra / Atlan  │
│  Lineage, Discovery, Classification         │
└──────────────────────────────────────────────────┘
          &amp;uarr; 메타데이터 수집 (Ingest)
┌──────────────────────────────────────────────────┐
│     기술 카탈로그 (Technical Layer)           │
│  HMS / Glue / Polaris / UC / Nessie / Gravitino │
│  스키마 관리, 엔진 인가, REST API, RBAC    │
└──────────────────────────────────────────────────┘
          &amp;uarr; 메타데이터 등록
┌──────────────────────────────────────────────────┐
│              스토리지 레이어                  │
│         S3 / GCS / ADLS / HDFS              │
│  Iceberg / Delta Lake / Hudi / Paimon       │
└──────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티 카탈로그 Federation 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 1 &amp;mdash; 쿼리 엔진 레벨 Federation&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Trino/Presto에서 여러 카탈로그를 catalog.properties로 동시 등록&lt;/li&gt;
&lt;li&gt;SELECT * FROM glue_catalog.db.table JOIN nessie_catalog.db.table 형태로 크로스 카탈로그 조인 가능&lt;/li&gt;
&lt;li&gt;운영 복잡도가 낙지만 쿼리 레이어에서만 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 2 &amp;mdash; 카탈로그 레이어 Federation (Gravitino 방식)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gravitino Metalake가 HMS&amp;middot;Glue&amp;middot;MySQL 등을 Connector로 흡수&lt;/li&gt;
&lt;li&gt;엔진은 Gravitino 단일 엔드포인트에만 연결&lt;/li&gt;
&lt;li&gt;양방향 direct management로 카탈로그 간 일관성 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;방법 3 &amp;mdash; 데이터 메시(Data Mesh) 분산 카탈로그&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인별 독립 카탈로그 운영 (Domain Ownership)&lt;/li&gt;
&lt;li&gt;중앙 거버넌스 카탈로그(OpenMetadata)가 각 도메인 카탈로그를 수집&amp;middot;조율&lt;/li&gt;
&lt;li&gt;자율성과 거버넌스를 동시에 달성하는 현대적 패턴&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Data Mesh에서의 카탈로그 역할&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data Mesh 아키텍처에서 카탈로그는 &lt;b&gt;데이터 제품(Data Product)&lt;/b&gt; 의 메타데이터를 관리하는 계약(contract) 레지스트리 역할을 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 도메인은 자체 기술 카탈로그(HMS&amp;middot;Iceberg REST&amp;middot;UC OSS 등)를 운영합니다.&lt;/li&gt;
&lt;li&gt;중앙 거버넌스 카탈로그는 각 도메인의 &lt;b&gt;데이터 제품 스펙, SLO, 오너십&lt;/b&gt;을 등록&amp;middot;검색합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache Gravitino의 Metalake 개념&lt;/b&gt;은 이 패턴과 잘 정합합니다 &amp;mdash; 도메인별 Catalog을 Metalake에서 연합 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Apache Gravitino + Snowflake 연동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gravitino와 Snowflake를 연동하는 방법은 두 가지이며, 기존 환경과 목표에 따라 선택합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 1 &amp;mdash; JDBC Catalog 페더레이션 (Snowflake &amp;rarr; Gravitino 데이터 소스 등록)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gravitino가 Snowflake JDBC 드라이버를 통해 Snowflake를 Catalog로 등록합니다. Trino&amp;middot;Spark 등 엔진이 Gravitino 단일 엔드포인트를 통해 Snowflake 테이블과 Iceberg&amp;middot;HMS 테이블을 크로스 조인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;: Snowflake 워크로드가 이미 존재하고, Gravitino로 다른 데이터 소스(HMS, Iceberg, MySQL 등)와 통합해야 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Gravitino REST API &amp;mdash; Snowflake 카탈로그 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;POST /api/metalakes/{metalake}/catalogs
{
  &quot;name&quot;: &quot;snowflake_catalog&quot;,
  &quot;type&quot;: &quot;RELATIONAL&quot;,
  &quot;provider&quot;: &quot;jdbc-postgresql&quot;,
  &quot;comment&quot;: &quot;Snowflake federation via Gravitino JDBC&quot;,
  &quot;properties&quot;: {
    &quot;jdbc-url&quot;: &quot;jdbc:snowflake://&amp;lt;account&amp;gt;.snowflakecomputing.com/?db=&amp;lt;database&amp;gt;&amp;amp;warehouse=&amp;lt;warehouse&amp;gt;&quot;,
    &quot;jdbc-user&quot;: &quot;&amp;lt;username&amp;gt;&quot;,
    &quot;jdbc-password&quot;: &quot;&amp;lt;password&amp;gt;&quot;,
    &quot;jdbc-driver&quot;: &quot;net.snowflake.client.jdbc.SnowflakeDriver&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Trino에서 크로스 소스 조인 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- Gravitino를 통해 Snowflake 테이블 + Iceberg 테이블 조인
SELECT s.customer_id, s.revenue, r.region_name
FROM gravitino.snowflake_catalog.sales_db.orders s
JOIN gravitino.iceberg_catalog.warehouse.dim_region r
  ON s.region_id = r.region_id;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 2 &amp;mdash; Iceberg REST (Gravitino &amp;rarr; Snowflake Open Catalog 노출)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gravitino가 관리하는 Iceberg 테이블 메타데이터를 Iceberg REST Catalog API로 노출하고, Snowflake Open Catalog(Polaris 기반)가 이를 구독하여 Snowflake가 동일 Iceberg 테이블을 External Iceberg Table로 직접 읽는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;: Iceberg 기반 레이크하우스를 Gravitino로 관리하고, Snowflake도 같은 테이블에 읽기 접근해야 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아키텍처 흐름&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Gravitino (Iceberg 메타데이터 관리 + 엔진 인가)
    ↕ Iceberg REST Catalog API
Snowflake Open Catalog (Polaris, Iceberg REST 클라이언트로 동기화)
    ↕ External Iceberg Table
Snowflake Warehouse (읽기 전용 쿼리)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Snowflake &amp;mdash; External Catalog 통합 설정 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 1. Gravitino Iceberg REST를 External Catalog로 등록
CREATE CATALOG INTEGRATION gravitino_iceberg
  CATALOG_SOURCE = ICEBERG_REST
  TABLE_FORMAT = ICEBERG
  CATALOG_NAMESPACE = 'warehouse'
  REST_CONFIG = (
    CATALOG_URI = 'https://&amp;lt;gravitino-host&amp;gt;:8090/iceberg/api'
    CATALOG_NAME = 'iceberg_catalog'
  )
  REST_AUTHENTICATION = (
    TYPE = OAUTH
    OAUTH_TOKEN_URI = 'https://&amp;lt;gravitino-host&amp;gt;:8090/oauth/token'
    OAUTH_CLIENT_ID = '&amp;lt;client_id&amp;gt;'
    OAUTH_CLIENT_SECRET = '&amp;lt;client_secret&amp;gt;'
    OAUTH_ALLOWED_SCOPES = ('PRINCIPAL_ROLE:ALL')
  )
  ENABLED = TRUE;

-- 2. Gravitino가 관리하는 Iceberg 테이블을 Snowflake External Table로 마운트
CREATE ICEBERG TABLE snowflake_db.public.orders
  EXTERNAL_VOLUME = 's3_iceberg_vol'
  CATALOG = 'gravitino_iceberg'
  CATALOG_TABLE_NAME = 'orders';

-- 3. Snowflake에서 조회
SELECT * FROM snowflake_db.public.orders LIMIT 100;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;방법 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 방법 1 &amp;mdash; JDBC Federation 방법 2 &amp;mdash; Iceberg REST&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake 역할&lt;/td&gt;
&lt;td&gt;데이터 소스 (Gravitino가 읽음)&lt;/td&gt;
&lt;td&gt;Iceberg 테이블 소비자 (Snowflake가 읽음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 흐름 방향&lt;/td&gt;
&lt;td&gt;Snowflake &amp;rarr; Gravitino &amp;rarr; 엔진&lt;/td&gt;
&lt;td&gt;Gravitino &amp;rarr; Snowflake Open Catalog &amp;rarr; Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake 쓰기&lt;/td&gt;
&lt;td&gt;Gravitino 경유 가능 (JDBC Write)&lt;/td&gt;
&lt;td&gt;Snowflake는 읽기 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구성 복잡도&lt;/td&gt;
&lt;td&gt;낮음 (JDBC 드라이버 설치 + REST 등록)&lt;/td&gt;
&lt;td&gt;중간 (Iceberg REST + OAuth 설정 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 상황&lt;/td&gt;
&lt;td&gt;기존 Snowflake 데이터를 다른 소스와 통합&lt;/td&gt;
&lt;td&gt;Iceberg 레이크하우스 + Snowflake 동시 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;락인 위험&lt;/td&gt;
&lt;td&gt;없음 (JDBC 표준)&lt;/td&gt;
&lt;td&gt;없음 (Iceberg REST 표준)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하이브리드 구성 &amp;mdash; 방법 1 + 방법 2 동시 운용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단방향의 한계를 행과적으로 해결하는 구성입니다. Snowflake 원체 데이터는 JDBC로 Gravitino가 읽어 Iceberg 레이크하우스에 올리고, 처리된 Iceberg 테이블은 Snowflake Open Catalog을 통해 Snowflake가 다시 읽는 주기를 형성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아키텍처 흐름&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[Snowflake 원체 데이터]
    &amp;darr; 방법 1 &amp;mdash; JDBC 읽기 (Gravitino JDBC Catalog)
[Gravitino]
    &amp;darr; Iceberg ETL 변환 &amp;middot; 저장 (Spark / Trino)
[S3 / GCS &amp;mdash; Iceberg 테이블]
    &amp;darr; 방법 2 &amp;mdash; Iceberg REST (Gravitino REST API)
[Snowflake Open Catalog (Polaris)]
    &amp;darr; External Iceberg Table
[Snowflake Warehouse &amp;mdash; 분석 쿼리]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Snowflake에 잘 정리된 판매 원렓 데이터를 JDBC로 가져와 Spark로 전처리(Cleansing, Aggregation)한 뒤 Iceberg로 저장&lt;/li&gt;
&lt;li&gt;처리된 Iceberg 마트 테이블을 Snowflake이 Open Catalog을 통해 다시 읽어 대시보드나 ML 입력 데이터로 활용&lt;/li&gt;
&lt;li&gt;Gravitino가 양쪽 커넥터를 단일 Metalake에서 관리하므로 엔진은 Gravitino 하나만 바라보면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하이브리드 방식에서의 양방향성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향 구현 방법 지원 여부&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake &amp;rarr; Gravitino (읽기)&lt;/td&gt;
&lt;td&gt;방법 1 JDBC Catalog&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake &amp;rarr; Gravitino (쓰기)&lt;/td&gt;
&lt;td&gt;방법 1 JDBC Write&lt;/td&gt;
&lt;td&gt;✅ (소량 쓰기 한정 권장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gravitino &amp;rarr; Snowflake (읽기)&lt;/td&gt;
&lt;td&gt;방법 2 Iceberg REST + External Table&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gravitino &amp;rarr; Snowflake (쓰기)&lt;/td&gt;
&lt;td&gt;Snowflake External Iceberg Table 한계&lt;/td&gt;
&lt;td&gt;❌ (불가)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snowflake에서 Iceberg 테이블에 쓰기가 필요한 경우 Snowflake-managed Iceberg Table을 쓰는 것이 유일한 선택이지만, 이 때는 메타데이터 소유권이 Snowflake로 넘어가 Gravitino가 모듨 마스터 역할을 잃게 됩니다. 대부분의 엔터프라이즈 환경에서는 &lt;b&gt;Gravitino가 카탈로그 주수, Snowflake는 소비자&lt;/b&gt;로 역할을 나누는 하이브리드 구성이 현실적인 최선안입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 요약&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제품 유형별 분류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오픈소스&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hive Metastore(HMS) &amp;mdash; 레거시 표준, 가장 광범위한 호환성&lt;/li&gt;
&lt;li&gt;Project Nessie &amp;mdash; Git-like 브랜칭, Multi-table Atomic Commit&lt;/li&gt;
&lt;li&gt;Apache Polaris &amp;mdash; Iceberg REST 표준 레퍼런스 구현, 벤더 중립&lt;/li&gt;
&lt;li&gt;Unity Catalog OSS &amp;mdash; 멀티 포맷 추상화, Iceberg REST 호환&lt;/li&gt;
&lt;li&gt;Apache Gravitino &amp;mdash; AI-native 유니버설 메타스토어, 4대 포맷(Iceberg&amp;middot;Delta&amp;middot;Hudi&amp;middot;Paimon) + ML 모델&amp;middot;벡터 통합 (ASF TLP)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라우드 매니지드&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS Glue Data Catalog &amp;mdash; AWS 생태계 표준, Iceberg 자동 최적화&lt;/li&gt;
&lt;li&gt;GCP Dataproc Metastore &amp;mdash; HMS 완벽 호환, GCP 레거시 워크로드&lt;/li&gt;
&lt;li&gt;Snowflake Open Catalog &amp;mdash; Polaris 기반, Iceberg REST 표준, 벤더 중립 매니지드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상용 SaaS&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Unity Catalog (Databricks) &amp;mdash; 가장 깊은 거버넌스, AI&amp;middot;ML 자산 포함&lt;/li&gt;
&lt;li&gt;Tabular &amp;mdash; Iceberg 전문 (Databricks 인수로 신규 채택 비권장)&lt;/li&gt;
&lt;li&gt;Dremio Arctic &amp;mdash; Nessie 기반 매니지드&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시나리오별 선택 가이드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시나리오 권장 제품&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS 생태계 + Iceberg 자동 최적화&lt;/td&gt;
&lt;td&gt;AWS Glue Data Catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCP + 레거시 Hive 워크로드 마이그레이션&lt;/td&gt;
&lt;td&gt;GCP Dataproc Metastore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks 중심 + 심화 거버넌스 필요&lt;/td&gt;
&lt;td&gt;Unity Catalog (상용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;멀티 클라우드 + Iceberg REST 표준 + 락인 회피&lt;/td&gt;
&lt;td&gt;Apache Polaris / Snowflake Open Catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg + 데이터 브랜칭&amp;middot;실험 워크플로우&lt;/td&gt;
&lt;td&gt;Project Nessie / Dremio Arctic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;셀프 호스팅 + 멀티 포맷 + 비용 최소화&lt;/td&gt;
&lt;td&gt;Unity Catalog OSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hudi/Paimon 중심 + 레거시 호환&lt;/td&gt;
&lt;td&gt;HMS (+ Glue/Dataproc Metastore 매니지드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-native 멀티 포맷 + 멀티 엔진 + 자체 호스팅&lt;/td&gt;
&lt;td&gt;Apache Gravitino&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2026년 기준 업계 방향성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 업계의 큰 흐름은 &lt;b&gt;Iceberg REST Catalog 표준 중심으로의 수렴&lt;/b&gt;입니다. HMS는 레거시 호환을 위해 유지되지만 신규 아키텍처 설계에서는 사용을 지양하는 추세이며, Apache Polaris가 레퍼런스 구현으로 자리 잡고 있습니다. Databricks는 Tabular 인수를 통해 Delta와 Iceberg를 통합하는 방향을 선택하였습니다. 멀티 포맷&amp;middot;멀티 엔진&amp;middot;벤더 중립을 모두 만족하는 완전한 오픈소스 솔루션은 아직 성숙 단계에 있으며, 엔터프라이즈는 대부분 오픈소스 카탈로그 + 매니지드 클라우드 서비스의 조합으로 운영하고 있습니다.&lt;/p&gt;</description>
      <category>공부/데이터</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/746</guid>
      <comments>https://brownbears.tistory.com/746#entry746comment</comments>
      <pubDate>Sun, 26 Apr 2026 23:58:50 +0900</pubDate>
    </item>
    <item>
      <title>Apache XTable이란</title>
      <link>https://brownbears.tistory.com/745</link>
      <description>&lt;h1&gt;개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache XTable&amp;trade;(Incubating)은 데이터 레이크하우스의 서로 다른 오픈 테이블 포맷 간 상호운용성을 제공하는 오픈소스 메타데이터 변환 도구입니다. Apache Iceberg, Apache Hudi, Delta Lake 세 가지 주요 포맷 사이에서 데이터를 복사하거나 이동하지 않고 메타데이터만 번역하여, 하나의 물리적 데이터 셋을 여러 포맷으로 동시에 읽을 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 &lt;b&gt;OneTable&lt;/b&gt;이라는 이름으로 Microsoft, Google, Onehouse가 공동으로 오픈소스화하였으며, 이후 Apache Software Foundation에 기증되어 현재 &lt;b&gt;Apache XTable&amp;trade; (Incubating)&lt;/b&gt; 으로 불리고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;핵심 포인트:&lt;/b&gt; XTable은 새로운 테이블 포맷이 아닙니다. 기존 포맷 간의 메타데이터 변환기입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Apache XTable이 필요한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 레이크하우스 생태계에는 세 가지 주요 오픈 테이블 포맷이 공존하고 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;포맷&lt;/th&gt;
&lt;th&gt;주요 강점&lt;/th&gt;
&lt;th&gt;주요 지지 업체&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apache Hudi&lt;/td&gt;
&lt;td&gt;고빈도 업서트, 낮은 레이턴시 스트리밍 수집&lt;/td&gt;
&lt;td&gt;Uber, AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Iceberg&lt;/td&gt;
&lt;td&gt;대규모 분석, 강력한 스키마 진화&lt;/td&gt;
&lt;td&gt;Netflix, Apple, Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;Databricks 최적화, ACID 트랜잭션&lt;/td&gt;
&lt;td&gt;Databricks, Microsoft&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 각 도구나 플랫폼이 특정 포맷만 지원하는 경우가 많다는 점입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Databricks는 Delta Lake에 최적화되어 있습니다.&lt;/li&gt;
&lt;li&gt;Snowflake는 주로 Iceberg를 지원합니다.&lt;/li&gt;
&lt;li&gt;Apache Hudi로 수집하는 팀과 Iceberg로 분석하는 팀이 같은 조직 안에 공존하기도 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;포맷 파편화(format fragmentation)&lt;/b&gt; 가 발생하면 동일한 데이터를 중복 저장하거나, 포맷을 전환할 때 전체 데이터를 재작성해야 하는 비용이 발생합니다. Apache XTable은 이 문제를 메타데이터 변환만으로 해결합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;아키텍처 및 동작 원리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache XTable는 다음 원칙으로 동작합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 파일은 그대로 유지됩니다.&lt;/b&gt; 모든 포맷은 공통적으로 Parquet 파일을 사용하며, XTable은 이 파일을 건드리지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메타데이터만 번역합니다.&lt;/b&gt; 각 포맷의 메타데이터 디렉토리(&lt;code&gt;_delta_log/&lt;/code&gt;, &lt;code&gt;metadata/&lt;/code&gt;, &lt;code&gt;.hoodie/&lt;/code&gt;)에 새 메타데이터를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Primary(원본) + Secondary(대상) 구조입니다.&lt;/b&gt; 쓰기는 Primary 포맷으로만 이루어지고, XTable이 Secondary 포맷의 메타데이터를 동기화합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 가지 동기화 모드:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Incremental(증분) 모드&lt;/b&gt;: 마지막 동기화 이후 변경된 커밋만 처리합니다. 대용량 테이블에 적합하며 성능이 우수합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Full(전체) 모드&lt;/b&gt;: 테이블 전체를 처음부터 재동기화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Apache XTable 장단점 및 한계&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 복사 없음&lt;/b&gt;: 물리적 데이터 파일을 이동하거나 복사하지 않아 스토리지 비용이 증가하지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전방향(omni-directional) 변환&lt;/b&gt;: Hudi &amp;harr; Iceberg &amp;harr; Delta Lake 간 어떤 방향으로도 변환이 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;점진적 마이그레이션 지원&lt;/b&gt;: 전환 기간 동안 두 포맷을 동시에 유지할 수 있어 무중단 마이그레이션이 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카탈로그 동기화&lt;/b&gt;: Hive Metastore, AWS Glue와의 메타데이터 등록 자동화를 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;증분 모드 효율성&lt;/b&gt;: 마지막 변경분만 처리하므로 대용량 테이블에서도 효율적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;벤더 중립&lt;/b&gt;: 특정 클라우드나 플랫폼에 종속되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점 및 한계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MoR(Merge-on-Read) 미지원&lt;/b&gt;: Hudi와 Iceberg의 MoR 테이블은 지원하지 않습니다. 로그 파일이 동기화되지 않아 데이터가 불완전하게 보일 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Delta Delete Vectors 미지원&lt;/b&gt;: Delta Lake의 Delete Vectors가 동기화되지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;쓰기 최적화 손실&lt;/b&gt;: Secondary 포맷 고유의 쓰기 최적화(Iceberg의 Hidden Partitioning, Delta의 Deletion Vectors 등)가 적용되지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Generated Columns 제한&lt;/b&gt;: Delta Lake의 Generated Columns 동기화가 제한적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타임스탬프 불일치&lt;/b&gt;: Secondary 포맷 커밋 타임스탬프가 Primary와 정확히 일치하지 않을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구조화 데이터 전용&lt;/b&gt;: 비정형 데이터는 지원하지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hudi 요구사항&lt;/b&gt;: Hudi를 소스로 사용할 경우 버전 0.14.0 이상, 메타데이터 테이블 및 Hive 스타일 파티셔닝 활성화가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 오버헤드&lt;/b&gt;: 별도 JAR 실행 및 동기화 스케줄 관리가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Delta Lake Uniform과의 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Delta Lake Uniform은 XTable과 자주 비교되는 기능입니다. 차이를 명확히 이해하고 선택해야 합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Apache XTable&lt;/th&gt;
&lt;th&gt;Delta Lake Uniform&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;변환 방향&lt;/td&gt;
&lt;td&gt;전방향 (Hudi&amp;harr;Iceberg&amp;harr;Delta)&lt;/td&gt;
&lt;td&gt;단방향 (Delta &amp;rarr; Iceberg/Hudi)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;소스 포맷&lt;/td&gt;
&lt;td&gt;Hudi, Iceberg, Delta 모두 가능&lt;/td&gt;
&lt;td&gt;Delta Lake만 소스 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks 의존성&lt;/td&gt;
&lt;td&gt;없음 (독립 실행)&lt;/td&gt;
&lt;td&gt;Databricks 플랫폼에 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운영 방식&lt;/td&gt;
&lt;td&gt;별도 JAR 실행 필요&lt;/td&gt;
&lt;td&gt;Delta 쓰기 시 자동 동기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liquid Clustering&lt;/td&gt;
&lt;td&gt;제약 없음&lt;/td&gt;
&lt;td&gt;함께 사용 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합한 케이스&lt;/td&gt;
&lt;td&gt;포맷 간 마이그레이션, 다중 포맷 유지, Hudi 중심 환경&lt;/td&gt;
&lt;td&gt;Databricks에서 쓰고 Snowflake에서 읽는 단순 패턴&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Apache XTable와 비슷한 오픈소스 및 매니지드 서비스&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XTable과 Delta Uniform이 해결하는 문제는 &lt;b&gt;&quot;데이터 복사 없이 여러 테이블 포맷을 동시에 지원하는 것&quot;&lt;/b&gt; 입니다. 이 관점에서 동일한 문제를 다루는 도구들을 정리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포맷 변환/번역 도구 (직접 비교 대상)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;방향&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Delta Lake Uniform&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta &amp;rarr; Iceberg / Hudi (단방향)&lt;/td&gt;
&lt;td&gt;Databricks에 내장. Delta 쓰기 시 자동 동기화. 별도 비교 섹션 참고.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Iceberg Snapshot / Migrate 프로시저&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Parquet &amp;middot; Hive &amp;middot; Delta &amp;rarr; Iceberg (단방향)&lt;/td&gt;
&lt;td&gt;Iceberg 자체 내장 기능. 운영 중 지속 동기화가 아닌 1회성 마이그레이션에 적합.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hudi Sync Tool (HoodieHiveSync 등)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hudi &amp;rarr; Hive Metastore / Iceberg (단방향)&lt;/td&gt;
&lt;td&gt;Hudi 내장. 커밋마다 외부 카탈로그에 메타데이터를 자동 등록. XTable보다 범위가 좁음.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다른 접근법으로 같은 문제 해결 (쿼리 레이어 통합)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타데이터를 번역하는 대신, 쿼리 엔진 레이어에서 여러 포맷을 직접 해석하는 방식입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Dremio&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg, Delta, Hudi, Parquet 등을 단일 쿼리 레이어로 통합. 포맷 변환 없이 다중 포맷 동시 접근.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Starburst / Trino&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Trino 기반으로 다양한 포맷 및 데이터 소스를 단일 SQL로 조회.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Apache Spark&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Hudi / Iceberg / Delta 모두 읽기 지원. 변환 없이 쿼리만으로 포맷 간 접근 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;매니지드 서비스 (XTable 기반 또는 포맷 상호운용 지원)&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Onehouse&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;XTable을 만든 회사의 매니지드 레이크하우스 서비스. XTable 동기화, 테이블 최적화, 멀티 포맷 수집을 완전 관리형으로 제공. AWS &amp;middot; GCP &amp;middot; Azure 지원.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS EMR + Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;EMR에서 XTable JAR를 실행하고 Glue Catalog에 멀티 포맷 테이블을 등록하는 조합으로 매니지드에 가깝게 운영 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;사용 예시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시 1: Hudi &amp;rarr; Iceberg + Delta 동시 동기화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hudi로 수집한 스트리밍 데이터를 Dremio(Iceberg)와 Databricks(Delta) 양쪽에서 동시에 읽어야 하는 경우입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계: Hudi 테이블 생성 (PySpark)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# pyspark --packages org.apache.hudi:hudi-spark3.2-bundle_2.12:0.14.0

from pyspark.sql.types import *

table_name = &quot;people&quot;
base_path  = &quot;s3://my-bucket/hudi-dataset&quot;

records = [
    (1, 'Alice', 25, 'Seoul', '2024-01-01 00:00:00'),
    (2, 'Bob',   30, 'Busan', '2024-01-01 00:00:00'),
]
schema = StructType([
    StructField(&quot;id&quot;,        IntegerType(), True),
    StructField(&quot;name&quot;,      StringType(),  True),
    StructField(&quot;age&quot;,       IntegerType(), True),
    StructField(&quot;city&quot;,      StringType(),  True),
    StructField(&quot;create_ts&quot;, StringType(),  True),
])

df = spark.createDataFrame(records, schema)

hudi_options = {
    'hoodie.table.name': table_name,
    'hoodie.datasource.write.partitionpath.field': 'city',
    'hoodie.datasource.write.hive_style_partitioning': 'true',
}

df.write.format(&quot;hudi&quot;).options(**hudi_options).save(f&quot;{base_path}/{table_name}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계: XTable 동기화 설정 (my_config.yaml)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;sourceFormat: HUDI
targetFormats:
  - DELTA
  - ICEBERG
datasets:
  - tableBasePath: s3://my-bucket/hudi-dataset/people
    tableName: people
    partitionSpec: city:VALUE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 동기화 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;java -jar xtable-utilities_2.12-0.2.0-SNAPSHOT-bundled.jar \
  --datasetConfig my_config.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 &lt;code&gt;s3://my-bucket/hudi-dataset/people/&lt;/code&gt; 아래에 &lt;code&gt;_delta_log/&lt;/code&gt;와 &lt;code&gt;metadata/&lt;/code&gt; 디렉토리가 생성됩니다. 데이터 파일은 변경되지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시 2: Delta &amp;rarr; Iceberg 마이그레이션 (무중단)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Delta Lake 테이블을 Iceberg로 전환 중이며, 전환 기간 동안 두 포맷을 모두 지원해야 하는 경우입니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;sourceFormat: DELTA
targetFormats:
  - ICEBERG
datasets:
  - tableBasePath: s3://my-bucket/delta-dataset/sales
    tableName: 2024_sales
    partitionSpec: partitionpath:department&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dremio에서 Iceberg 테이블 등록&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;CALL dremio.system.register_table(
  table        =&amp;gt; 'analytics.sales_2024',
  metadata_file =&amp;gt; 's3://my-bucket/delta-dataset/sales/metadata/v1.metadata.json'
);&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시 3: 실시간 스트리밍 파이프라인 패턴&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hudi로 실시간 업서트를 수행하고, 다운스트림 소비는 Iceberg로 제공하는 패턴입니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[Kafka] &amp;rarr; [Spark Structured Streaming] &amp;rarr; [Hudi 쓰기 (low-latency upsert)]
                                                       &amp;darr;
                                        [XTable 증분 동기화 (스케줄 실행)]
                                                       &amp;darr;
                                          [Iceberg 메타데이터 생성]
                                                       &amp;darr;
                              [Snowflake / Dremio / AWS Athena 에서 읽기]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴을 통해 Hudi의 고속 수집 능력과 Iceberg의 넓은 쿼리 엔진 지원을 동시에 활용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시 4: Iceberg &amp;rarr; Delta Lake 변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Iceberg로 운영 중인 테이블을 Databricks에서도 읽어야 하는 경우입니다. Iceberg를 Primary로 유지하면서 Delta Lake 메타데이터를 동기화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1단계: Iceberg 테이블 생성 (PySpark)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;pyspark \
  --packages org.apache.iceberg:iceberg-spark-runtime-3.2_2.12:1.4.1 \
  --conf &quot;spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions&quot; \
  --conf &quot;spark.sql.catalog.spark_catalog=org.apache.iceberg.spark.SparkSessionCatalog&quot; \
  --conf &quot;spark.sql.catalog.spark_catalog.type=hadoop&quot; \
  --conf &quot;spark.sql.catalog.spark_catalog.warehouse=s3://my-bucket/iceberg-warehouse&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from pyspark.sql.types import *

table_name = &quot;orders&quot;
warehouse  = &quot;s3://my-bucket/iceberg-warehouse&quot;

records = [
    (1001, 'Alice', 'laptop',   1200000, '2024-03-01'),
    (1002, 'Bob',   'monitor',   450000, '2024-03-01'),
    (1003, 'Carol', 'keyboard',   89000, '2024-03-02'),
]
schema = StructType([
    StructField(&quot;order_id&quot;,   IntegerType(), True),
    StructField(&quot;customer&quot;,   StringType(),  True),
    StructField(&quot;product&quot;,    StringType(),  True),
    StructField(&quot;amount&quot;,     IntegerType(), True),
    StructField(&quot;order_date&quot;, StringType(),  True),
])

df = spark.createDataFrame(records, schema)

df.write \
  .format(&quot;iceberg&quot;) \
  .partitionBy(&quot;order_date&quot;) \
  .saveAsTable(f&quot;spark_catalog.default.{table_name}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 &lt;code&gt;s3://my-bucket/iceberg-warehouse/default/orders/&lt;/code&gt; 아래에 &lt;code&gt;metadata/&lt;/code&gt;와 &lt;code&gt;data/&lt;/code&gt;가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2단계: XTable 동기화 설정 (my_config.yaml)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;sourceFormat: ICEBERG
targetFormats:
  - DELTA
datasets:
  - tableBasePath: s3://my-bucket/iceberg-warehouse/default/orders
    tableDataPath: s3://my-bucket/iceberg-warehouse/default/orders/data
    tableName: orders&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ &lt;b&gt;&lt;code&gt;tableDataPath&lt;/code&gt; 필수&lt;/b&gt;: Iceberg는 메타데이터(&lt;code&gt;metadata/&lt;/code&gt;)와 데이터(&lt;code&gt;data/&lt;/code&gt;) 디렉토리가 분리되어 있으므로 반드시 &lt;code&gt;tableDataPath&lt;/code&gt;를 명시해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 동기화 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;java -jar xtable-utilities_2.12-0.2.0-SNAPSHOT-bundled.jar \
  --datasetConfig my_config.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 같은 경로에 &lt;code&gt;_delta_log/&lt;/code&gt;가 추가됩니다. Parquet 데이터 파일은 변경되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4단계: Databricks에서 Delta로 읽기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# Databricks 노트북에서 실행

# 방법 1: 경로 직접 지정
df = spark.read.format(&quot;delta&quot;).load(
    &quot;s3://my-bucket/iceberg-warehouse/default/orders&quot;
)
df.show()

# 방법 2: 테이블로 등록 후 SQL 사용
spark.sql(&quot;&quot;&quot;
  CREATE TABLE IF NOT EXISTS orders_via_delta
  USING DELTA
  LOCATION 's3://my-bucket/iceberg-warehouse/default/orders'
&quot;&quot;&quot;)

spark.sql(&quot;&quot;&quot;
  SELECT * FROM orders_via_delta WHERE order_date = '2024-03-01'
&quot;&quot;&quot;).show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 S3 경로에 Iceberg와 Delta 메타데이터가 공존하며, Parquet 파일은 하나만 존재합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;XTable 운영 가이드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XTable 자체는 단순한 CLI 도구이지만, 프로덕션 환경에서는 주기적 실행, 카탈로그 등록, 모니터링을 어떻게 설계할지가 중요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실행 모드&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;모드&lt;/th&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;적합한 상황&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;일회성 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;java -jar xtable.jar --datasetConfig config.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;초기 마이그레이션, 수동 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스케줄 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;cron / Airflow / EventBridge 등으로 주기 호출&lt;/td&gt;
&lt;td&gt;배치성 파이프라인, 허용 지연이 분 단위 이상인 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;연속 실행 (RunSync)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;수집 태스크 직후 자동 트리거&lt;/td&gt;
&lt;td&gt;수집 후 즉시 동기화가 필요한 스트리밍 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 패턴 1: cron 스케줄 (가장 단순)&lt;/h2&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# crontab -e

# 15분마다 증분 동기화
*/15 * * * * java -jar /opt/xtable/xtable.jar \
  --datasetConfig /etc/xtable/config.yaml \
  &amp;gt;&amp;gt; /var/log/xtable/sync.log 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배치성 파이프라인이 실행되는 EMR, Dataproc 등의 워커 노드에 배포하는 가장 단순한 패턴입니다. 동기화 주기는 데이터 신선도 요구 수준에 맞춰 조정합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 패턴 2: AWS Lambda + EventBridge (서버리스)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 관리 없이 자동화하는 권장 패턴입니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[EventBridge 스케줄 (1시간마다)]
          &amp;darr;
[Detector Lambda] &amp;rarr; Glue Catalog 스캔
  (xtable_table_type, xtable_target_formats 태그 보유 테이블 탐지)
          &amp;darr; 비동기 호출
[Converter Lambda &amp;times; N개 테이블]
  각 테이블별로 XTable JAR 실행
          &amp;darr;
[S3에 Secondary 포맷 메타데이터 생성 + Glue Catalog 자동 등록]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Glue 테이블에 XTable 태그 설정 (테이블별 1회)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;glue_client.update_table(
    DatabaseName='my_db',
    TableInput={
        'Name': 'orders',
        'Parameters': {
            'xtable_table_type':     'ICEBERG',   # Primary 포맷
            'xtable_target_formats': 'DELTA',     # 변환 대상
        }
    }
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Detector Lambda가 Glue를 스캔하여 위 태그가 있는 테이블만 자동으로 변환합니다. 새 테이블 추가 시 태그만 달면 이후 자동 관리됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 패턴 3: Apache Airflow DAG&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 파이프라인이 Airflow로 관리되는 환경에서 XTable 동기화를 파이프라인에 통합하는 패턴입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime

with DAG(
    dag_id='xtable_sync',
    schedule_interval='*/30 * * * *',  # 30분마다
    start_date=datetime(2024, 1, 1),
    catchup=False,
) as dag:

    sync_orders = BashOperator(
        task_id='sync_orders_iceberg_to_delta',
        bash_command=(
            'java -jar /opt/xtable/xtable.jar '
            '--datasetConfig /opt/xtable/configs/orders.yaml'
        ),
    )

    sync_users = BashOperator(
        task_id='sync_users_hudi_to_iceberg',
        bash_command=(
            'java -jar /opt/xtable/xtable.jar '
            '--datasetConfig /opt/xtable/configs/users.yaml'
        ),
    )

    # 두 테이블은 독립적이므로 병렬 실행
    [sync_orders, sync_users]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집(ingest) 태스크 다음에 XTable 동기화 태스크를 연결하면 &lt;b&gt;수집 완료 후 즉시 동기화&lt;/b&gt;를 보장할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;증분(Incremental) vs 전체(Full) 동기화&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;동작&lt;/th&gt;
&lt;th&gt;사용 시점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Incremental&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;마지막 동기화 이후 새 커밋만 처리&lt;/td&gt;
&lt;td&gt;일반 운영 (기본값)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Full&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전체 커밋 재처리&lt;/td&gt;
&lt;td&gt;Incremental 실패 시 자동 폴백, 또는 테이블 재구성 시 수동 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XTable은 Incremental 실패 시 &lt;b&gt;자동으로 Full로 폴백&lt;/b&gt;하므로 별도 예외 처리 코드가 필요하지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;카탈로그 동기화 (CatalogSync)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XTable은 메타데이터 파일 생성 후 외부 카탈로그에 자동 등록하는 CatalogSync도 지원합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;카탈로그&lt;/th&gt;
&lt;th&gt;지원 포맷&lt;/th&gt;
&lt;th&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;AWS Glue&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg, Delta, Hudi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;catalogImpl: glue&lt;/code&gt; 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Hive Metastore&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Iceberg, Delta, Hudi&lt;/td&gt;
&lt;td&gt;HMS 주소 + &lt;code&gt;catalogImpl: hms&lt;/code&gt; 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Unity Catalog&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로드맵 예정&lt;/td&gt;
&lt;td&gt;현재 미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카탈로그 동기화를 활성화하면 Athena, Spark SQL, Trino 등에서 수동 등록 없이 즉시 조회할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모니터링 포인트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;S3에서 메타데이터 생성 확인&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;# Iceberg &amp;rarr; Delta 동기화 후 Delta 메타데이터 존재 확인
aws s3 ls s3://my-bucket/iceberg-warehouse/default/orders/_delta_log/

# Hudi &amp;rarr; Iceberg 동기화 후 Iceberg 메타데이터 확인
aws s3 ls s3://my-bucket/hudi-dataset/people/metadata/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 경보 조건&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;_delta_log/&lt;/code&gt; 또는 &lt;code&gt;metadata/&lt;/code&gt;가 예상 주기보다 오래 업데이트되지 않는 경우 &amp;rarr; XTable 프로세스 상태 확인&lt;/li&gt;
&lt;li&gt;Incremental 실패로 Full Sync 폴백이 반복되는 경우 &amp;rarr; 커밋 히스토리 크기 및 실행 시간 검토&lt;/li&gt;
&lt;li&gt;Primary 포맷 커밋 수와 Secondary 커밋 수의 차이가 커지는 경우 &amp;rarr; 동기화 주기 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영 시 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Secondary 포맷으로는 절대 쓰지 않습니다.&lt;/b&gt; XTable이 생성한 메타데이터를 외부 도구가 덮어쓰면 데이터 일관성이 깨집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Full Sync 비용 관리&lt;/b&gt;: Full Sync는 커밋 수에 비례해 시간이 소요됩니다. 일반 운영은 Incremental로만 사용하고, Full은 장애 복구 시에만 수동 실행하는 것이 권장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MoR 테이블 회피&lt;/b&gt;: Hudi/Iceberg의 Merge-on-Read 테이블은 XTable에서 지원하지 않습니다. CoW로 설정하거나 별도 전략을 검토하십시오.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;첫 동기화 후 검증 필수&lt;/b&gt;: 변환된 포맷으로 실제 쿼리를 실행하여 데이터 정합성을 확인하는 절차를 반드시 거치십시오.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;요약&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache XTable은 데이터 레이크하우스의 포맷 파편화 문제를 메타데이터 변환만으로 해결하는 도구입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;내용&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;핵심 가치&lt;/td&gt;
&lt;td&gt;데이터 복사 없이 Hudi&amp;harr;Iceberg&amp;harr;Delta 상호 운용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;동작 방식&lt;/td&gt;
&lt;td&gt;Primary 포맷으로 쓰기 &amp;rarr; XTable이 Secondary 포맷 메타데이터 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;적합한 케이스&lt;/td&gt;
&lt;td&gt;멀티 포맷 팀, 포맷 마이그레이션, 스트리밍+분석 혼합 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주의 사항&lt;/td&gt;
&lt;td&gt;MoR 테이블, Delete Vectors, Generated Columns 미지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대안&lt;/td&gt;
&lt;td&gt;Delta Uniform(단방향), Iceberg Migrate, Dremio/Trino&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;현재 상태&lt;/td&gt;
&lt;td&gt;Apache Software Foundation Incubating 프로젝트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제 XTable을 선택해야 할까요?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조직 내 여러 팀이 서로 다른 레이크하우스 포맷을 사용하는 경우&lt;/li&gt;
&lt;li&gt;포맷을 전환하면서도 다운타임 없이 기존 워크로드를 유지해야 하는 경우&lt;/li&gt;
&lt;li&gt;Hudi로 수집하고 Iceberg/Delta로 분석하는 패턴이 필요한 경우&lt;/li&gt;
&lt;li&gt;특정 벤더에 종속되지 않고 여러 플랫폼에서 동일한 데이터에 접근해야 하는 경우&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공부/데이터</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/745</guid>
      <comments>https://brownbears.tistory.com/745#entry745comment</comments>
      <pubDate>Sun, 26 Apr 2026 23:44:08 +0900</pubDate>
    </item>
    <item>
      <title>[Spark] 스파크 Structured Streaming 정리</title>
      <link>https://brownbears.tistory.com/744</link>
      <description>&lt;h1&gt;1. 개요&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark Structured Streaming은 Apache Spark 2.0에서 도입되고 2.2에서 정식 안정화된 &lt;b&gt;확장 가능한 스트림 처리 엔진&lt;/b&gt;입니다. 기존 배치 처리에서 사용하던 DataFrame/Dataset API를 그대로 스트리밍에 적용할 수 있어 &quot;한 번 작성, 배치/스트리밍 모두 실행&quot;이 가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 설계 철학: Unbounded Table 모델&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스파크 Structured Streaming은 입력 데이터 스트림을 &lt;b&gt;무한히 추가되는 테이블&lt;/b&gt;로 개념화합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로 도착하는 레코드 = 테이블에 추가되는 새 행(row)&lt;/li&gt;
&lt;li&gt;이 입력 테이블에 대한 쿼리가 &lt;b&gt;결과 테이블(Result Table)&lt;/b&gt; 을 생성&lt;/li&gt;
&lt;li&gt;결과 테이블은 주기적으로 싱크(Sink)에 기록됨&lt;/li&gt;
&lt;li&gt;이 추상화 덕분에 배치 쿼리와 완전히 동일한 로직으로 스트리밍 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;# 기본 예시: 소켓에서 단어 수 집계
spark = SparkSession.builder.appName(&quot;WordCount&quot;).getOrCreate()

lines = spark.readStream \
    .format(&quot;socket&quot;) \
    .option(&quot;host&quot;, &quot;localhost&quot;) \
    .option(&quot;port&quot;, 9999) \
    .load()

wordCounts = lines.select(explode(split(lines.value, &quot; &quot;)).alias(&quot;word&quot;)) \
                  .groupBy(&quot;word&quot;).count()

query = wordCounts.writeStream \
    .outputMode(&quot;complete&quot;) \
    .format(&quot;console&quot;) \
    .start()

query.awaitTermination()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Spark Structured Streaming 장단점&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Unified Batch/Streaming API&lt;/b&gt;: 동일한 DataFrame/Dataset API로 배치와 스트리밍 코드를 작성할 수 있습니다. 기존 배치 파이프라인을 스트리밍으로 전환할 때 코드 변경이 최소화됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Exactly-Once 보장&lt;/b&gt;: 마이크로 배치 모드에서 end-to-end exactly-once 처리를 기본 제공합니다. Delta Lake와 조합 시 멱등 쓰기(idempotent write)로 중복 레코드를 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fault Tolerance (체크포인트)&lt;/b&gt;: 체크포인트를 통한 자동 장애 복구를 지원합니다. 최근에는 changelog 기반 체크포인팅이 도입되어 전체 상태 스냅샷 대신 변경분만 저장해 I/O가 대폭 감소했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Catalyst Optimizer 활용&lt;/b&gt;: 스트리밍 쿼리에도 Catalyst 쿼리 최적화 엔진이 적용됩니다. 조건 푸시다운, 파티션 프루닝 등 배치 최적화 기법이 스트리밍에도 자동 적용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Delta Lake 완벽 통합&lt;/b&gt;: Delta Lake를 소스/싱크로 직접 사용할 수 있습니다. ACID 트랜잭션, 스키마 진화, 타임 트래블 등 Lakehouse 기능을 풀로 활용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;높은 확장성&lt;/b&gt;: 수십~수백 노드 클러스터로 수평 확장이 가능하며, 안정 조건에서 초당 최대 100만 이벤트 처리를 달성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;단점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Micro-Batch 레이턴시&lt;/b&gt;: 기본 마이크로 배치 모델의 레이턴시는 수백 ms~수 초 수준으로, Flink의 ms급에 비해 불리합니다. 다만 2025년 12월 Spark 4.1에서 &lt;b&gt;Real-Time Mode(RTM)&lt;/b&gt; 가 공개되어 p99 레이턴시 한 자릿수 ms가 달성되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 Stateful 연산&lt;/b&gt;: &lt;code&gt;mapGroupsWithState&lt;/code&gt;/&lt;code&gt;flatMapGroupsWithState&lt;/code&gt; 사용 시 상태 스키마나 타임아웃 타입을 변경하면 기존 체크포인트가 무효화됩니다. 또한 &lt;code&gt;shuffle.partitions&lt;/code&gt; 수를 스트림 시작 후에는 변경할 수 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메모리 관리 복잡성&lt;/b&gt;: State Store가 메모리와 디스크 리소스를 점유하며, 상태 크기가 커질수록 GC 압박 및 지연이 증가합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 Checkpoint 관리&lt;/b&gt;: 입력 소스 추가, Kafka 토픽 변경, stateful 연산 타입 변경 등이 발생하면 새 체크포인트가 필요합니다. 장기 운영 시 체크포인트 디렉토리 용량이 증가하여 주기적인 관리가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운영 비용&lt;/b&gt;: 스트리밍 특성상 클러스터를 24/7 상시 운영해야 하므로 배치 대비 고정 비용이 높습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spark Structured Streaming vs Apache Flink&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Spark Structured Streaming&lt;/th&gt;
&lt;th&gt;Apache Flink&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;처리 모델&lt;/td&gt;
&lt;td&gt;마이크로 배치 (기본) / RTM(Spark 4.1+)&lt;/td&gt;
&lt;td&gt;네이티브 이벤트 스트리밍&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;레이턴시&lt;/td&gt;
&lt;td&gt;수백 ms ~ 수 초 (RTM: 단 자릿수 ms)&lt;/td&gt;
&lt;td&gt;기본 ms 이하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exactly-Once&lt;/td&gt;
&lt;td&gt;기본 지원&lt;/td&gt;
&lt;td&gt;기본 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stateful 처리&lt;/td&gt;
&lt;td&gt;제한적 (스키마 변경 불가)&lt;/td&gt;
&lt;td&gt;강력 (세밀한 시간/상태 제어)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배치 통합&lt;/td&gt;
&lt;td&gt;완전 통합 (Unified API)&lt;/td&gt;
&lt;td&gt;별도 배치 API (제한적)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lakehouse 통합&lt;/td&gt;
&lt;td&gt;Delta Lake 완벽 통합&lt;/td&gt;
&lt;td&gt;상대적으로 약함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 기준&lt;/b&gt;: 레이턴시 허용 + Lakehouse/배치 통합 필요 &amp;rarr; Spark Structured Streaming / ms급 레이턴시 필수 + 복잡한 stateful 연산 &amp;rarr; Apache Flink&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 핵심 개념 및 내부 아키텍처&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;처리 모드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Micro-Batch (기본)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 시간 간격 또는 데이터 가용성에 따라 이산(discrete) 배치로 처리합니다. 드라이버가 매 배치마다 소스를 폴링하고 새 오프셋을 결정하여 플랜을 생성하고 실행합니다. end-to-end 레이턴시는 최소 ~100ms이며 exactly-once를 보장합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Continuous Processing (Spark 2.3+)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레코드가 도착하는 즉시 처리하며, epoch 기반 체크포인팅을 사용합니다. 레이턴시는 ~1ms이지만 at-least-once만 보장하며, stateless 단순 변환(map, filter)만 지원합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Real-Time Mode (Spark 4.1, 2025년 12월 출시)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로 배치와 달리 처리 스테이지가 동시에 실행되며, 셔플 파일이 생성되는 즉시 리듀서가 시작됩니다. epoch 경계는 복구 북마크로만 사용되고, p99 레이턴시 한 자릿수 ms를 달성했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Trigger 모드&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;구문&lt;/th&gt;
&lt;th&gt;동작&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.trigger()&lt;/code&gt; 없음&lt;/td&gt;
&lt;td&gt;이전 배치가 끝나는 즉시 새 배치 시작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProcessingTime&lt;/td&gt;
&lt;td&gt;&lt;code&gt;processingTime=&quot;2 seconds&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;고정 시간 간격 스케줄링&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Once&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Trigger.Once()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;가용 데이터 처리 후 종료 (Deprecated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AvailableNow&lt;/td&gt;
&lt;td&gt;&lt;code&gt;availableNow=True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;가용 데이터를 증분 배치로 처리 후 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continuous&lt;/td&gt;
&lt;td&gt;&lt;code&gt;continuous=&quot;1 second&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;연속 처리, N초마다 체크포인트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Output Modes&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 테이블의 어느 행이 싱크에 기록될지를 결정합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;모드&lt;/th&gt;
&lt;th&gt;동작&lt;/th&gt;
&lt;th&gt;사용 시기&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Append&lt;/b&gt; (기본)&lt;/td&gt;
&lt;td&gt;마지막 트리거 이후 새로 추가된 행만 기록&lt;/td&gt;
&lt;td&gt;집계 없는 쿼리, 워터마크가 있는 집계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Update&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;마지막 트리거 이후 변경된 행만 기록&lt;/td&gt;
&lt;td&gt;집계 (쓰기 볼륨 최소화)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Complete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전체 결과 테이블을 매 트리거마다 재기록&lt;/td&gt;
&lt;td&gt;집계 (전체 상태가 항상 필요한 경우)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;# Update 모드 예시
wordCounts.writeStream.outputMode(&quot;update&quot;).format(&quot;kafka&quot;) \
    .option(&quot;kafka.bootstrap.servers&quot;, &quot;localhost:9092&quot;) \
    .option(&quot;topic&quot;, &quot;output&quot;).start()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Watermarking과 Late Data 처리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 네트워크 지연, 재시도 등으로 인해 순서가 뒤바뀌어 도착할 수 있습니다. Watermark 없이는 엔진이 모든 이벤트 타임 윈도우의 상태를 영구적으로 유지해야 하므로 메모리가 무한히 증가합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동작 원리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;withWatermark(이벤트 시간 컬럼, 지연 임계값)&lt;/code&gt; 으로 설정&lt;/li&gt;
&lt;li&gt;엔진이 수신된 모든 이벤트에서 &lt;code&gt;최대 이벤트 시간&lt;/code&gt;을 추적&lt;/li&gt;
&lt;li&gt;&lt;b&gt;워터마크 = 최대 이벤트 시간 - 임계값&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이벤트 시간이 워터마크보다 이전인 데이터는 &quot;너무 늦은 데이터&quot;로 간주되어 드롭&lt;/li&gt;
&lt;li&gt;워터마크를 지난 윈도우의 상태는 자동 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;from pyspark.sql.functions import window

# 최대 10분까지 지연 도착을 허용
windowedCounts = events \
    .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;) \
    .groupBy(
        window(&quot;eventTime&quot;, &quot;10 minutes&quot;, &quot;5 minutes&quot;),  # 10분 윈도우, 5분 슬라이드
        &quot;userId&quot;
    ) \
    .count()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 규칙&lt;/b&gt;: &lt;code&gt;withWatermark&lt;/code&gt;는 반드시 &lt;code&gt;groupBy&lt;/code&gt; &lt;b&gt;이전&lt;/b&gt;에 적용해야 합니다. 다중 스트리밍 소스가 있는 쿼리에서 전역 워터마크는 개별 소스 워터마크 중 &lt;b&gt;최솟값&lt;/b&gt;으로 결정됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. Stateful Operations (상태 기반 연산)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내장 Stateful 연산자&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;groupBy().count()&lt;/code&gt;, &lt;code&gt;groupBy().agg()&lt;/code&gt; &amp;mdash; 실행 집계&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dropDuplicates()&lt;/code&gt; &amp;mdash; 중복 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;join()&lt;/code&gt; (스트림-스트림 조인) &amp;mdash; 매칭을 위한 버퍼 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;임의 상태 처리 (Arbitrary Stateful Processing)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;mapGroupsWithState (Spark 2.2+)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹 키별 사용자 정의 상태를 유지하며, 트리거마다 그룹당 &lt;b&gt;정확히 하나&lt;/b&gt;의 출력 행을 반환해야 합니다. 실행 중 총합, 고정 출력 형태의 세션 추적 등에 활용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;flatMapGroupsWithState (Spark 2.2+)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mapGroupsWithState&lt;/code&gt;와 유사하지만 그룹당 &lt;b&gt;0개 이상&lt;/b&gt;의 행을 반환할 수 있습니다. 세션 윈도우, 복잡 이벤트 처리(CEP) 등에 활용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;transformWithState (Spark 4.0+ &amp;mdash; 차세대 API)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mapGroupsWithState&lt;/code&gt;/&lt;code&gt;flatMapGroupsWithState&lt;/code&gt;를 대체하는 차세대 API로, 객체 지향 방식의 &lt;code&gt;StatefulProcessor&lt;/code&gt; 클래스를 구현합니다. &lt;code&gt;ValueState&lt;/code&gt;, &lt;code&gt;ListState&lt;/code&gt;, &lt;code&gt;MapState&lt;/code&gt; 등 풍부한 상태 프리미티브와 TTL 기반 상태 소멸, 키별 다중 타이머, 스키마 진화를 지원합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. Sources &amp;amp; Sinks&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 소스&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;소스&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;주요 옵션&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;kafka&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kafka.bootstrap.servers&lt;/code&gt;, &lt;code&gt;subscribe&lt;/code&gt;, &lt;code&gt;startingOffsets&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;delta&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;path&lt;/code&gt;, &lt;code&gt;maxFilesPerTrigger&lt;/code&gt;, &lt;code&gt;ignoreChanges&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;parquet&quot;&lt;/code&gt;, &lt;code&gt;&quot;json&quot;&lt;/code&gt; 등&lt;/td&gt;
&lt;td&gt;&lt;code&gt;path&lt;/code&gt;, &lt;code&gt;maxFilesPerTrigger&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;rate&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rowsPerSecond&lt;/code&gt; (테스트용)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 싱크&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;싱크&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;출력 모드&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;kafka&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append, Update, Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;delta&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append, Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;parquet&quot;&lt;/code&gt; 등&lt;/td&gt;
&lt;td&gt;Append only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForeachBatch&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;모두 지원 (가장 유연)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;# ForeachBatch: 다중 싱크에 동시 쓰기
def write_to_multiple(batch_df, batch_id):
    batch_df.persist()
    batch_df.write.format(&quot;delta&quot;).mode(&quot;append&quot;).save(&quot;/delta/output&quot;)
    batch_df.write.format(&quot;jdbc&quot;).mode(&quot;append&quot;) \
        .option(&quot;url&quot;, &quot;jdbc:postgresql://...&quot;).save()
    batch_df.unpersist()

df.writeStream.foreachBatch(write_to_multiple) \
    .option(&quot;checkpointLocation&quot;, &quot;/checkpoints/multi&quot;) \
    .start()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Exactly-Once 보장 및 Fault Tolerance&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark Structured Streaming은 세 가지 핵심 요소로 exactly-once를 보장합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;재생 가능한 소스(Replayable Sources)&lt;/b&gt;: Kafka는 특정 오프셋부터, Delta Lake는 특정 트랜잭션 버전부터 데이터를 재생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;멱등 싱크(Idempotent Sinks)&lt;/b&gt;: 동일 데이터를 여러 번 기록해도 결과가 같아야 합니다. 파일 싱크는 원자적 파일 생성(임시 파일 &amp;rarr; 이름 변경)으로, Delta Lake는 트랜잭션 로그로 이를 보장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;체크포인팅 + Write-Ahead Log(WAL)&lt;/b&gt;: 오프셋 로그(소스에서 읽은 위치), 커밋 로그(싱크에 완전히 커밋된 배치), 상태 스토어(집계&amp;middot;조인 상태)를 체크포인트에 저장합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;query = df.writeStream \
    .format(&quot;delta&quot;) \
    .option(&quot;checkpointLocation&quot;, &quot;/checkpoints/my-query&quot;) \  # 필수
    .start(&quot;/delta/output&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장애 복구 흐름&lt;/h2&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;장애 감지 &amp;rarr; 체크포인트에서 쿼리 재시작 &amp;rarr; 마지막 커밋 오프셋부터 소스 재생 &amp;rarr; 변환 재실행 &amp;rarr; 멱등 싱크에 기록&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Spark Structured Streaming vs DStream (레거시)&lt;/h1&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;DStream (Spark Streaming)&lt;/th&gt;
&lt;th&gt;Structured Streaming&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;RDD 기반 (&lt;code&gt;DStream[T]&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;DataFrame/Dataset API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도입&lt;/td&gt;
&lt;td&gt;Spark 0.7&lt;/td&gt;
&lt;td&gt;Spark 2.0 (안정 2.2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Deprecated&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;현행 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최적화&lt;/td&gt;
&lt;td&gt;없음 (수동 RDD 연산)&lt;/td&gt;
&lt;td&gt;Catalyst + Tungsten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event-Time&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;기본 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermarking&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;&lt;code&gt;withWatermark()&lt;/code&gt; 내장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보장 수준&lt;/td&gt;
&lt;td&gt;At-least-once&lt;/td&gt;
&lt;td&gt;Exactly-once (마이크로 배치)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;최소 레이턴시&lt;/td&gt;
&lt;td&gt;~500ms&lt;/td&gt;
&lt;td&gt;~100ms (마이크로 배치) / ~1ms (연속 처리)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스트림-스트림 조인&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;기본 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. EMR에서 사용 예시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배포 방식 선택&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;장점&lt;/th&gt;
&lt;th&gt;단점&lt;/th&gt;
&lt;th&gt;권장 시나리오&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EMR on EC2&lt;/td&gt;
&lt;td&gt;Spot 활용 최대화, 세밀한 제어&lt;/td&gt;
&lt;td&gt;클러스터 관리 부담&lt;/td&gt;
&lt;td&gt;대용량 안정 워크로드, 비용 최적화 최우선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMR on EKS&lt;/td&gt;
&lt;td&gt;K8s 환경 통합, 멀티 테넌시&lt;/td&gt;
&lt;td&gt;K8s 운영 지식 필요&lt;/td&gt;
&lt;td&gt;EKS 인프라를 이미 보유한 팀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMR Serverless&lt;/td&gt;
&lt;td&gt;인프라 Zero, 자동 스케일&lt;/td&gt;
&lt;td&gt;콜드 스타트, 일부 제약&lt;/td&gt;
&lt;td&gt;빠른 도입, 예측 불가 워크로드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EMR Serverless 클러스터 생성 및 잡 제출&lt;/h2&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;# 1단계: EMR Serverless Application 생성
aws emr-serverless create-application \
  --name &quot;streaming-app&quot; \
  --type SPARK \
  --release-label emr-7.2.0 \
  --initial-capacity '{
    &quot;DRIVER&quot;: {&quot;workerCount&quot;: 1, &quot;workerConfiguration&quot;: {&quot;cpu&quot;: &quot;4vCPU&quot;, &quot;memory&quot;: &quot;16GB&quot;}},
    &quot;EXECUTOR&quot;: {&quot;workerCount&quot;: 10, &quot;workerConfiguration&quot;: {&quot;cpu&quot;: &quot;4vCPU&quot;, &quot;memory&quot;: &quot;16GB&quot;}}
  }'

# 2단계: STREAMING 모드로 잡 제출
aws emr-serverless start-job-run \
  --application-id &amp;lt;APPLICATION_ID&amp;gt; \
  --execution-role-arn arn:aws:iam::ACCOUNT_ID:role/EMRServerlessRole \
  --mode 'STREAMING' \
  --job-driver '{
    &quot;sparkSubmit&quot;: {
      &quot;entryPoint&quot;: &quot;s3://my-bucket/scripts/kafka_to_s3.py&quot;,
      &quot;sparkSubmitParameters&quot;: &quot;--packages org.apache.spark:spark-sql-kafka-0-10_2.12:3.5.1,software.amazon.msk:aws-msk-iam-auth:2.2.0&quot;
    }
  }' \
  --retry-policy '{&quot;maxFailedAttemptsPerHour&quot;: 5}'&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MSK Kafka &amp;rarr; S3 파이프라인 (PySpark)&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession
from pyspark.sql.functions import col, from_json, year, month, dayofmonth
from pyspark.sql.types import *

spark = SparkSession.builder.appName(&quot;KafkaToS3&quot;).getOrCreate()

schema = StructType([
    StructField(&quot;order_id&quot;, StringType(), True),
    StructField(&quot;customer_id&quot;, StringType(), True),
    StructField(&quot;amount&quot;, DoubleType(), True),
    StructField(&quot;event_time&quot;, TimestampType(), True),
])

# MSK Kafka 소스 (IAM 인증)
raw_df = spark.readStream \
    .format(&quot;kafka&quot;) \
    .option(&quot;kafka.bootstrap.servers&quot;, &quot;&amp;lt;MSK_BOOTSTRAP&amp;gt;:9098&quot;) \
    .option(&quot;subscribe&quot;, &quot;orders&quot;) \
    .option(&quot;kafka.security.protocol&quot;, &quot;SASL_SSL&quot;) \
    .option(&quot;kafka.sasl.mechanism&quot;, &quot;AWS_MSK_IAM&quot;) \
    .option(&quot;kafka.sasl.jaas.config&quot;,
            &quot;software.amazon.msk.auth.iam.IAMLoginModule required;&quot;) \
    .option(&quot;kafka.sasl.client.callback.handler.class&quot;,
            &quot;software.amazon.msk.auth.iam.IAMClientCallbackHandler&quot;) \
    .load()

parsed_df = raw_df \
    .select(from_json(col(&quot;value&quot;).cast(&quot;string&quot;), schema).alias(&quot;data&quot;)) \
    .select(&quot;data.*&quot;) \
    .withWatermark(&quot;event_time&quot;, &quot;10 minutes&quot;) \
    .withColumn(&quot;year&quot;, year(&quot;event_time&quot;)) \
    .withColumn(&quot;month&quot;, month(&quot;event_time&quot;)) \
    .withColumn(&quot;day&quot;, dayofmonth(&quot;event_time&quot;))

query = parsed_df.writeStream \
    .format(&quot;parquet&quot;) \
    .option(&quot;path&quot;, &quot;s3://my-bucket/output/&quot;) \
    .option(&quot;checkpointLocation&quot;, &quot;s3://my-bucket/checkpoints/&quot;) \
    .partitionBy(&quot;year&quot;, &quot;month&quot;, &quot;day&quot;) \
    .trigger(processingTime=&quot;5 minutes&quot;) \
    .start()

query.awaitTermination()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kinesis Data Streams 연동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EMR 7.1.0부터 Kinesis Connector가 릴리즈 이미지에 내장되어 별도 JAR가 필요 없습니다. &lt;code&gt;GetRecords&lt;/code&gt;(공유 처리량)와 &lt;code&gt;SubscribeToShard&lt;/code&gt;(Enhanced Fan-Out, 저지연 전용 처리량) 두 가지 모드를 지원합니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;kinesis_df = spark.readStream \
    .format(&quot;aws-kinesis&quot;) \
    .option(&quot;kinesis.region&quot;, &quot;ap-northeast-2&quot;) \
    .option(&quot;kinesis.streamName&quot;, &quot;sensor-data-stream&quot;) \
    .option(&quot;kinesis.consumerType&quot;, &quot;GetRecords&quot;) \
    .option(&quot;kinesis.startingposition&quot;, &quot;LATEST&quot;) \
    .load()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS 서비스 통합 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AWS 서비스&lt;/th&gt;
&lt;th&gt;연동 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MSK (Kafka)&lt;/td&gt;
&lt;td&gt;spark-sql-kafka + aws-msk-iam-auth JAR, IAM 인증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis Data Streams&lt;/td&gt;
&lt;td&gt;aws-kinesis 커넥터 (EMR 7.1+ 내장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;EMRFS(S3A 내장), &lt;code&gt;s3://&lt;/code&gt; 경로 직접 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue Data Catalog&lt;/td&gt;
&lt;td&gt;Hive Metastore Factory Class 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모니터링: CloudWatch 연동&lt;/h2&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;from pyspark.sql.streaming import StreamingQueryListener
import boto3

class CloudWatchListener(StreamingQueryListener):
    def __init__(self):
        self.cw = boto3.client(&quot;cloudwatch&quot;, region_name=&quot;ap-northeast-2&quot;)

    def onQueryProgress(self, event):
        progress = event.progress
        self.cw.put_metric_data(
            Namespace=&quot;SparkStreaming/Production&quot;,
            MetricData=[
                {&quot;MetricName&quot;: &quot;InputRowsPerSecond&quot;,
                 &quot;Value&quot;: progress.inputRowsPerSecond or 0,
                 &quot;Unit&quot;: &quot;Count/Second&quot;,
                 &quot;Dimensions&quot;: [{&quot;Name&quot;: &quot;JobName&quot;, &quot;Value&quot;: &quot;kafka-to-s3&quot;}]},
            ]
        )
    def onQueryStarted(self, event): pass
    def onQueryTerminated(self, event): pass

spark.streams.addListener(CloudWatchListener())&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. Dataproc에서 사용 예시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클러스터 생성&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;gcloud dataproc clusters create my-streaming-cluster \
  --region=us-central1 \
  --master-machine-type=n2-standard-4 \
  --num-workers=4 \
  --worker-machine-type=n2-standard-4 \
  --image-version=2.2-debian12 \
  --enable-component-gateway \
  --metric-sources=spark \
  --properties=&quot;spark:spark.streaming.stopGracefullyOnShutdown=true&quot; \
  --project=my-project&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의&lt;/b&gt;: Dataproc의 Autoscaling은 Spark Structured Streaming을 지원하지 않습니다. 스트리밍 클러스터에서는 autoscaling 없이 고정 클러스터를 운영하는 것이 권장됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Managed Kafka &amp;rarr; BigQuery 파이프라인 (PySpark)&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession
from pyspark.sql.functions import col, from_json, current_timestamp
from pyspark.sql.types import *

spark = SparkSession.builder.appName(&quot;KafkaToBigQuery&quot;).getOrCreate()

schema = StructType([
    StructField(&quot;user_id&quot;, StringType(), True),
    StructField(&quot;event_type&quot;, StringType(), True),
    StructField(&quot;amount&quot;, DoubleType(), True),
    StructField(&quot;event_time&quot;, LongType(), True),
])

raw_df = spark.readStream \
    .format(&quot;kafka&quot;) \
    .option(&quot;kafka.bootstrap.servers&quot;,
            &quot;bootstrap.CLUSTER_ID.us-central1.managedkafka.PROJECT.cloud:9092&quot;) \
    .option(&quot;kafka.security.protocol&quot;, &quot;SASL_SSL&quot;) \
    .option(&quot;kafka.sasl.mechanism&quot;, &quot;OAUTHBEARER&quot;) \
    .option(&quot;subscribe&quot;, &quot;user-events&quot;) \
    .option(&quot;startingOffsets&quot;, &quot;latest&quot;) \
    .load()

parsed_df = raw_df \
    .select(from_json(col(&quot;value&quot;).cast(&quot;string&quot;), schema).alias(&quot;data&quot;)) \
    .select(&quot;data.*&quot;) \
    .withColumn(&quot;ingested_at&quot;, current_timestamp())

def write_to_bigquery(batch_df, epoch_id):
    batch_df.write \
        .format(&quot;bigquery&quot;) \
        .option(&quot;writeMethod&quot;, &quot;direct&quot;) \
        .option(&quot;table&quot;, &quot;my-project.analytics.events&quot;) \
        .mode(&quot;append&quot;) \
        .save()

query = parsed_df.writeStream \
    .foreachBatch(write_to_bigquery) \
    .option(&quot;checkpointLocation&quot;, &quot;gs://my-bucket/checkpoints/kafka-to-bq&quot;) \
    .trigger(processingTime=&quot;30 seconds&quot;) \
    .start()

query.awaitTermination()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCS에 Parquet으로 쓰기&lt;/h2&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;query = df.writeStream \
    .format(&quot;parquet&quot;) \
    .outputMode(&quot;append&quot;) \
    .option(&quot;path&quot;, &quot;gs://my-bucket/data/events/&quot;) \
    .option(&quot;checkpointLocation&quot;, &quot;gs://my-bucket/checkpoints/gcs-parquet&quot;) \
    .trigger(processingTime=&quot;5 minutes&quot;) \
    .partitionBy(&quot;process_date&quot;, &quot;process_hour&quot;) \
    .start()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCP 서비스 통합 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GCP 서비스&lt;/th&gt;
&lt;th&gt;연동 방식&lt;/th&gt;
&lt;th&gt;비고&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Managed Kafka&lt;/td&gt;
&lt;td&gt;표준 Kafka 커넥터, OAUTHBEARER 인증&lt;/td&gt;
&lt;td&gt;Pub/Sub Lite 대체 권장 (2026년 종료 예정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BigQuery&lt;/td&gt;
&lt;td&gt;spark-bigquery-connector, &lt;code&gt;writeMethod=direct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dataproc 2.1+ 내장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gs://&lt;/code&gt; 경로 직접 사용&lt;/td&gt;
&lt;td&gt;체크포인트/싱크 모두 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bigtable&lt;/td&gt;
&lt;td&gt;spark-bigtable-connector (v1.1+)&lt;/td&gt;
&lt;td&gt;Data Boost로 분석 부하 분리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잡 제출&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;gcloud dataproc jobs submit pyspark gs://my-bucket/jobs/streaming.py \
  --cluster=my-streaming-cluster \
  --region=us-central1 \
  --packages=org.apache.spark:spark-sql-kafka-0-10_2.12:3.5.0 \
  --properties=spark.executor.memory=4g,spark.executor.cores=2,spark.executor.instances=8&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 데이터브릭스에서 사용 예시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DLT vs 일반 Structured Streaming 선택 기준&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Structured Streaming 권장&lt;/th&gt;
&lt;th&gt;Delta Live Tables (DLT) 권장&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;foreachBatch로 외부 DB 쓰기&lt;/td&gt;
&lt;td&gt;멀티-스테이지 ETL 파이프라인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;세밀한 클러스터 제어 필요&lt;/td&gt;
&lt;td&gt;데이터 품질 거버넌스 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLA-민감 레이턴시 파이프라인&lt;/td&gt;
&lt;td&gt;자동 체크포인트&amp;middot;스케일링 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특수 소스/싱크 필요&lt;/td&gt;
&lt;td&gt;Unity Catalog 기반 거버넌스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Auto Loader (cloudFiles)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Loader는 S3, ADLS, GCS에 새로 도착하는 파일을 자동으로 감지하여 점진적으로 처리하는 Databricks 전용 Structured Streaming 소스입니다. 시간당 수백만 개 파일 처리가 가능하며, Directory Listing Mode(기본)와 File Notification Mode(대규모 권장) 두 가지를 지원합니다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;# Auto Loader: S3 JSON &amp;rarr; Delta Lake
streaming_df = (
    spark.readStream
        .format(&quot;cloudFiles&quot;)
        .option(&quot;cloudFiles.format&quot;, &quot;json&quot;)
        .option(&quot;cloudFiles.schemaLocation&quot;, &quot;s3://my-bucket/checkpoints/schema/&quot;)
        .option(&quot;cloudFiles.inferColumnTypes&quot;, &quot;true&quot;)
        .option(&quot;recursiveFileLookup&quot;, &quot;true&quot;)
        .load(&quot;s3://my-bucket/raw/events/&quot;)
)

query = (
    streaming_df.writeStream
        .format(&quot;delta&quot;)
        .outputMode(&quot;append&quot;)
        .option(&quot;checkpointLocation&quot;, &quot;s3://my-bucket/checkpoints/events/&quot;)
        .option(&quot;mergeSchema&quot;, &quot;true&quot;)
        .trigger(processingTime=&quot;30 seconds&quot;)
        .toTable(&quot;catalog.bronze.raw_events&quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kafka &amp;rarr; Delta Lake 파이프라인 (전체 예시)&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from pyspark.sql.functions import from_json, col, current_timestamp
from pyspark.sql.types import *

event_schema = StructType([
    StructField(&quot;order_id&quot;, StringType(), False),
    StructField(&quot;user_id&quot;, StringType(), True),
    StructField(&quot;price&quot;, StringType(), True),
    StructField(&quot;event_time&quot;, TimestampType(), True),
])

kafka_options = {
    &quot;kafka.bootstrap.servers&quot;: &quot;kafka-broker:9092&quot;,
    &quot;subscribe&quot;: &quot;orders-topic&quot;,
    &quot;startingOffsets&quot;: &quot;latest&quot;,
    &quot;kafka.security.protocol&quot;: &quot;SASL_SSL&quot;,
    &quot;kafka.sasl.mechanism&quot;: &quot;PLAIN&quot;,
    # Databricks Secret 사용 (하드코딩 금지)
    &quot;kafka.sasl.jaas.config&quot;: (
        &quot;kafkashaded.org.apache.kafka.common.security.plain.PlainLoginModule required &quot;
        f&quot;username='{dbutils.secrets.get('kafka', 'username')}' &quot;
        f&quot;password='{dbutils.secrets.get('kafka', 'password')}'; &quot;
    ),
    &quot;maxOffsetsPerTrigger&quot;: &quot;50000&quot;,
    &quot;failOnDataLoss&quot;: &quot;false&quot;,
}

raw_kafka_df = spark.readStream.format(&quot;kafka&quot;).options(**kafka_options).load()

parsed_df = (
    raw_kafka_df
        .select(from_json(col(&quot;value&quot;).cast(&quot;string&quot;), event_schema).alias(&quot;data&quot;),
                col(&quot;offset&quot;), col(&quot;timestamp&quot;).alias(&quot;kafka_timestamp&quot;))
        .select(&quot;data.*&quot;, &quot;offset&quot;, &quot;kafka_timestamp&quot;, current_timestamp().alias(&quot;ingested_at&quot;))
        .filter(col(&quot;order_id&quot;).isNotNull())
)

query = (
    parsed_df.writeStream
        .format(&quot;delta&quot;)
        .outputMode(&quot;append&quot;)
        .option(&quot;checkpointLocation&quot;, &quot;s3://my-bucket/checkpoints/kafka-orders/&quot;)
        .option(&quot;mergeSchema&quot;, &quot;true&quot;)
        .trigger(processingTime=&quot;10 seconds&quot;)
        .toTable(&quot;catalog.bronze.orders_raw&quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Delta MERGE INTO (Upsert / CDC)&lt;/h2&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;def upsert_to_delta(micro_batch_df, batch_id):
    micro_batch_df.createOrReplaceTempView(&quot;batch_updates&quot;)
    micro_batch_df.sparkSession.sql(&quot;&quot;&quot;
        MERGE INTO catalog.silver.customers AS target
        USING (
            SELECT * FROM (
                SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY updated_at DESC) AS rn
                FROM batch_updates
            ) WHERE rn = 1
        ) AS source
        ON target.customer_id = source.customer_id
        WHEN MATCHED AND source.op = 'D' THEN DELETE
        WHEN MATCHED THEN UPDATE SET *
        WHEN NOT MATCHED AND source.op != 'D' THEN INSERT *
    &quot;&quot;&quot;)

spark.readStream.table(&quot;catalog.bronze.customer_cdc&quot;) \
    .writeStream \
    .foreachBatch(upsert_to_delta) \
    .option(&quot;checkpointLocation&quot;, &quot;s3://my-bucket/checkpoints/customers/&quot;) \
    .trigger(processingTime=&quot;2 minutes&quot;) \
    .start()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Delta Live Tables (DLT) 파이프라인&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import dlt
from pyspark.sql.functions import col, current_timestamp

# Bronze: 원본 인제스트
@dlt.table(name=&quot;bronze_orders&quot;, comment=&quot;Auto Loader로 S3에서 수집한 원본 주문 데이터&quot;)
def bronze_orders():
    return (
        spark.readStream.format(&quot;cloudFiles&quot;)
            .option(&quot;cloudFiles.format&quot;, &quot;json&quot;)
            .option(&quot;cloudFiles.schemaLocation&quot;, &quot;/pipelines/bronze_orders/schema&quot;)
            .load(&quot;s3://my-bucket/raw/orders/&quot;)
            .withColumn(&quot;_ingested_at&quot;, current_timestamp())
    )

# Silver: 정제 + 품질 검증
@dlt.table(name=&quot;silver_orders&quot;)
@dlt.expect(&quot;order_id_not_null&quot;, &quot;order_id IS NOT NULL&quot;)
@dlt.expect_or_drop(&quot;valid_amount&quot;, &quot;amount &amp;gt; 0&quot;)
@dlt.expect_or_fail(&quot;valid_event&quot;, &quot;event_type IN ('CREATE', 'UPDATE', 'CANCEL')&quot;)
def silver_orders():
    return (
        dlt.read_stream(&quot;bronze_orders&quot;)
            .withColumn(&quot;amount&quot;, col(&quot;amount&quot;).cast(&quot;decimal(18,2)&quot;))
            .withColumn(&quot;event_date&quot;, col(&quot;event_time&quot;).cast(&quot;date&quot;))
    )

# Gold: 집계
@dlt.table(name=&quot;gold_daily_revenue&quot;)
def gold_daily_revenue():
    return (
        dlt.read(&quot;silver_orders&quot;)
            .filter(col(&quot;event_type&quot;) == &quot;CREATE&quot;)
            .groupBy(&quot;event_date&quot;)
            .agg({&quot;amount&quot;: &quot;sum&quot;, &quot;order_id&quot;: &quot;count&quot;})
    )&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비용 최적화 전략&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;th&gt;효과&lt;/th&gt;
&lt;th&gt;방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job Cluster 사용&lt;/td&gt;
&lt;td&gt;유휴 비용 제거&lt;/td&gt;
&lt;td&gt;All-purpose 대신 Job Cluster로 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spot Instances&lt;/td&gt;
&lt;td&gt;최대 80% 절감&lt;/td&gt;
&lt;td&gt;Worker 노드에 Spot 적용, Driver는 On-demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;availableNow&lt;/code&gt; Trigger&lt;/td&gt;
&lt;td&gt;배치화로 비용 최소화&lt;/td&gt;
&lt;td&gt;연속 스트리밍 대신 주기적 실행 후 자동 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DLT Enhanced Autoscaling&lt;/td&gt;
&lt;td&gt;부하에 따라 자동 축소&lt;/td&gt;
&lt;td&gt;DLT 파이프라인 설정에서 활성화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# availableNow: 배치 패턴으로 비용 최적화
query = (
    spark.readStream.format(&quot;cloudFiles&quot;).option(&quot;cloudFiles.format&quot;, &quot;parquet&quot;)
        .load(&quot;s3://my-bucket/raw/&quot;)
    .writeStream.format(&quot;delta&quot;)
        .option(&quot;checkpointLocation&quot;, &quot;/checkpoints/batch/&quot;)
        .trigger(availableNow=True)  # 처리 완료 후 자동 종료 &amp;rarr; 클러스터 자동 종료
        .toTable(&quot;catalog.silver.processed&quot;)
)
query.awaitTermination()&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 런타임&lt;/b&gt;: Databricks Runtime 17.3 LTS (현재 최신 LTS, 2025.10 출시). 프로덕션 스트리밍에는 LTS 버전 사용을 강력 권장합니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>공부</category>
      <author>불곰1</author>
      <guid isPermaLink="true">https://brownbears.tistory.com/744</guid>
      <comments>https://brownbears.tistory.com/744#entry744comment</comments>
      <pubDate>Sun, 26 Apr 2026 23:37:09 +0900</pubDate>
    </item>
  </channel>
</rss>