들어가며: 데이터가 넘쳐도 답을 찾지 못하는 역설
Cloudflare는 초당 10억 개 이상의 이벤트를 처리하는 글로벌 네트워크를 운영합니다. 하지만 몇 년 전만 해도 내부 분석가가 "오늘 가입한 도메인 중 트래픽 Top 100에 드는 도메인이 몇 개인가?"라는 질문에 답하려면, Postgres, ClickHouse, BigQuery, R2, Kafka 등 수많은 시스템을 오가며 각각의 자격 증명과 쿼리 언어를 익혀야 했습니다. 데이터는 넘쳐나는데, 정작 필요한 데이터를 찾는 것은 극도로 어려운 상황이었죠.
이 문제를 해결하기 위해 Cloudflare는 두 가지 내부 도구를 직접 만들었습니다. Town Lake(통합 데이터 분석 플랫폼)와 Skipper(그 위에서 동작하는 AI 데이터 에이전트)입니다. 이 글은 그 구축 과정에서 얻은 인사이트를 공유합니다. 특히 데이터 플랫폼을 설계할 때 고려해야 할 거버넌스, 확장성, AI 활용 전략에 초점을 맞춥니다.

Town Lake: 데이터 레이크하우스의 실전 설계
Town Lake의 핵심 아키텍처는 데이터 레이크하우스입니다. 객체 스토리지(R2)에 데이터를 저장하고, 그 위에 메타데이터 레이어를 얹어 데이터베이스처럼 동작하게 만드는 구조입니다.
주요 컴포넌트
- 쿼리 엔진 (Apache Trino): 단일 SQL 쿼리로 Postgres, ClickHouse, R2의 Iceberg 테이블을 조인할 수 있습니다.
- R2 Data Catalog (Iceberg): 스키마 진화, 타임 트래블, 파티션 관리 등 데이터 레이크의 핵심 기능을 제공합니다.
- DataHub: 메타데이터 카탈로그로, 모든 테이블/컬럼/소유자/라인리지 정보를 관리합니다.
- Lifeguard: 접근 제어 서비스로, D1에 규칙을 저장하고 사용자 그룹 정보를 동적으로 반영합니다.
- Skimmer: PII 탐지 스캐너로, Workers AI를 활용해 컬럼별 민감 정보를 자동 분류합니다.
- Transformer: Workflows 기반 ELT 엔진으로, SQL 변환 DAG를 정의하고 실행합니다.
-- 예시: 단일 쿼리로 여러 소스 조인
SELECT
c.customer_name,
SUM(b.billed_amount) AS total_revenue
FROM fct.billings_allocated b
JOIN dim.accounts a ON b.account_id = a.account_id
JOIN dim.customers c ON a.customer_id = c.customer_id
WHERE b.billing_period = '2024-Q4'
GROUP BY c.customer_name
ORDER BY total_revenue DESC
LIMIT 100;
이 구조의 장점은 데이터를 이동하지 않고도 여러 시스템을 하나로 묶을 수 있다는 점입니다. 특히 쿼리 푸시다운을 통해 ClickHouse에 필터를 위임하고, Postgres의 차원 테이블과 조인하며, R2의 빌링 롤업을 랭킹하는 등 복잡한 작업을 단일 쿼리로 처리합니다.

Skipper: 자연어로 데이터를 묻는 AI 에이전트
Skipper는 자연어 질문을 받아 올바른 테이블을 찾고, SQL을 작성하고, 결과를 검증하는 AI 에이전트입니다. 핵심은 다층적 컨텍스트를 제공하는 것입니다.
- 스키마 및 사용 메타데이터: DataHub가 모든 컬럼/타입/조인 관계를 제공합니다.
- 인간 주석: 테이블 소유자가 작성한 설명을 컨텍스트로 활용합니다.
- 코드 유래 지식: 테이블을 생성하는 SQL 파이프라인의
.meta.json문서를 자동으로 등록합니다. - 큐레이션된 데이터 모델: 소수의 휴먼 작성 문서로 에이전트의 우선순위를 안내합니다.
- 런타임 인트로스펙션: 필요시
DESCRIBE나SELECT DISTINCT같은 쿼리를 직접 실행해 검증합니다.
Code Mode: MCP 서버의 새로운 패턴
Skipper의 MCP 서버는 30여 개의 개별 도구를 노출하는 대신, search와 execute 두 가지만 노출합니다. 모델은 JavaScript 스니펫을 작성해 전체 도구 세트를 프로그래밍 방식으로 호출합니다.
// 예시: MCP Code Mode에서 여러 단계를 한 번에 실행
const datasets = await skipper.search_datasets({ query: "billing product revenue" });
const queryId = await skipper.start_query({ sql: "SELECT ..." });
const results = await skipper.fetch_results({ queryId, mode: "inject" });
return skipper.create_chart({ chartType: "bar", data: results.rows, ... });
이 방식은 모델이 복잡한 워크플로우를 단일 왕복으로 표현할 수 있게 해주며, 감사 가능한 코드로 기록됩니다.

한국 개발 생태계에서의 적용 맥락
국내 기업에서도 데이터 플랫폼 구축은 비슷한 어려움을 겪습니다. 특히 금융권이나 커머스에서는 개인정보보호법 등 규제로 인해 PII 관리가 더욱 중요합니다. Town Lake의 default-closed 방식, 즉 테이블이 검토되기 전에는 접근을 차단하는 정책은 국내 규제 환경에 잘 맞는 접근법입니다. 다만, 초기 구축 시 리뷰 병목이 생길 수 있으므로 자동화된 PII 분류기와 같은 도구를 도입하는 것이 필수적입니다.
주의사항 및 한계
- 초기 구축 비용: Trino, Iceberg, DataHub 등 여러 오픈소스 컴포넌트를 조합해야 하므로 운영 복잡도가 높습니다.
- 메모리 레이어의 중요성: 에이전트가 반복적인 실수를 줄이기 위해서는 장기 메모리(예: D1)가 필수입니다.
- 보안은 설계의 일부: 접근 제어와 감사 로깅을 후순위로 미루면 결국 데이터 플랫폼 전체의 신뢰도가 떨어집니다.
다음 단계 학습 방향
- Apache Iceberg와 Trino의 심화 활용법을 익혀보세요.
- MCP 서버와 Code Mode 패턴을 직접 구현해보며 에이전트 개발 경험을 쌓아보세요.
- 데이터 거버넌스 프레임워크(예: DataHub)를 실제 프로젝트에 적용해보는 것을 추천합니다.
결론
Cloudflare의 사례는 단순히 기술적 성공담이 아니라, 데이터 문화를 바꾸는 여정입니다. 데이터 플랫폼을 '백오피스'로 취급하지 않고 핵심 인프라로 승격시킨 점이 가장 큰 교훈입니다. 여러분의 조직에서도 데이터를 하나의 창구로 통합하고, AI 에이전트를 통해 접근성을 높이는 시도를 해보시길 권합니다. 더 자세한 내용은 Cloudflare 블로그 원문을 참고하세요.