JOIN까지 내 컴퓨터에서 실행하는 도구다. 저장소가 내건 한 줄은 이것 — "One SQL interface over APIs, files, and live sources — built for agents."(API·파일·라이브 소스 위의 단일 SQL 인터페이스, 에이전트를 위해 만든). 먼저 오해를 풀자 — 이건 새 데이터베이스가 아니다. 데이터를 어디론가 옮겨 담는 게 아니라, 이미 흩어져 있는 소스들을 "테이블처럼 보이게" 감싸서 질의하는 데이터 페더레이션 엔진이다. 심장부는 Apache DataFusion(러스트 SQL 쿼리 엔진)이다.
두 번째로 각인할 것 — "에이전트를 위해" 만들었다는 점. AI 에이전트가 API마다 툴을 갈아 끼우며 수십 번 호출하는 대신, coral에 붙은 MCP 서버로 SQL 한 방을 던지게 한다. 이 문서는 소스를 직접 클론해 "이 저장소에서 무엇을 배울 수 있는가"를 파고든다.
(저장소: withcoral/coral · Rust(edition 2024) · Apache-2.0 · v0.6.0 · 최신 커밋 2026-07-17 · Apache DataFusion 기반 · 이번 회차는 TrendShift Daily·Weekly·Monthly가 모두 기분석 소진되어 OSSInsight 순위로 폴백)
withcoral/coral은 내 컴퓨터에서 도는 로컬 우선(local-first) SQL 런타임이다. GitHub API, 로컬 CSV/Parquet 파일, 다른 MCP 서버 같은 서로 다른 데이터 소스를 전부 "테이블"처럼 보이게 감싼 뒤, 평범한 SQL로 질의하게 해 준다. 예컨대 SELECT * FROM github.issues WHERE repo='coral' 한 줄이 실제로는 GitHub REST API 호출로 바뀌어 실행된다.
가장 먼저 풀어야 할 오해 — coral은 데이터를 저장하는 새 데이터베이스가 아니다. Postgres나 MySQL처럼 데이터를 안에 쌓아 두는 게 아니라, 이미 다른 곳(웹 API·파일·서비스)에 사는 데이터를 그 자리에서 SQL로 질의한다. 이렇게 "여러 이질적 소스를 옮기지 않고 한 창구로 질의하는" 기술을 데이터 페더레이션(data federation)이라 부른다. coral은 그 엔진을 Apache DataFusion(러스트로 짠 SQL 쿼리 엔진) 위에 자체 커넥터를 얹어 구현했다.
JOIN이 걸리면 필요한 만큼만 각 소스를 호출해 로컬(내 컴퓨터)에서 이어 붙인다. 창고를 새로 짓지 않아 항상 실시간 최신 데이터를 본다.TableProvider로 꽂아 넣게 열어 둔다. 내부 데이터는 Apache Arrow(컬럼형 인메모리 포맷)로 흐른다. coral은 이 확장점에 "HTTP API를 읽는 TableProvider", "파일을 읽는 TableProvider", "MCP 서버를 읽는 TableProvider"를 끼워 넣어 SQL-over-anything을 완성했다.에이전트 입장에서 GitHub·Linear·로컬 파일은 저마다 말이 다른 가게들이다 — 인증 방식도, 페이지 넘기는 법도, 응답 모양(JSON 구조)도 전부 다르다. 지금까지 에이전트는 가게마다 다른 점원(툴)을 일일이 상대하며 "이건 이렇게 물어봐야 하고 저건 저렇게…"를 외워야 했다. coral은 그 사이에 만능 사서를 세운다.
이 사서에게는 "모든 가게를 표(테이블)로 보여 달라"고만 하면 된다. 그러면 에이전트는 가게별 방언을 몰라도, 누구나 아는 공용어인 SQL로 "이 표에서 저 표를 이어 붙여 이런 조건으로 뽑아 줘"라고 한 번에 시킬 수 있다. 사서는 뒤에서 각 가게에 알맞은 방식으로 물어보고(API 호출·파일 읽기), 결과를 표로 정리해 돌려준다. 방언 통역은 사서가, 논리는 SQL이 담당한다.
coral이 트렌딩에 오른 이유는 "또 하나의 쿼리 엔진"이어서가 아니다. AI 에이전트가 데이터를 다루는 방식의 고질병을 정면으로 겨냥했기 때문이다. 지금 에이전트는 여러 API를 쓰려면 소스마다 툴을 갈아 끼우고, 인증·페이지네이션·재시도 로직을 매번 새로 짜고, 서로 다른 JSON을 힘겹게 이어 붙인다. 이 과정에서 툴 호출이 폭증하고, 토큰이 낭비되고, 크로스 소스(소스 간) 추론은 깨지기 쉽다. coral은 이 모든 걸 단일 SQL 인터페이스로 대체하겠다고 나선다.
| 포인트 | 내용 |
|---|---|
| 정체 | 에이전트를 위한 로컬 우선 SQL 런타임. API·파일·라이브 소스를 SQL 테이블로 노출하고 소스 간 JOIN을 로컬에서 실행. Apache DataFusion 위에 자체 커넥터·옵티마이저를 얹은 154K줄 규모의 러스트 프로젝트. |
| 차별점 ① | 읽기 전용(read layer) 설계. 페이지네이션을 엔진이 알아서 처리하고 JSON 덩어리 대신 정돈된 테이블 행을 반환. 필요한 컬럼만 SELECT하니 API 트래픽·토큰이 준다. |
| 차별점 ② | 소스를 선언적 YAML로 정의. 코드를 짜지 않고 manifest.yaml에 "이 API의 이 경로가 이 테이블"이라고 적으면 커넥터가 된다. OpenAPI 명세를 import해 자동 생성도 가능. |
| 차별점 ③ | 크로스 소스 JOIN을 진짜로 최적화. 한 소스의 결과값을 다른 소스 API의 필터로 밀어 넣는 dependent join을 DataFusion 커스텀 옵티마이저로 구현(뒤에서 상술). |
| 제작자 · 라이선스 | withcoral 조직(withcoral.com·Discord 보유). Apache-2.0(관대한 라이선스). 러스트 크레이트 전부 coral-* 네이티브 네이밍 — 다른 프로젝트의 포크·리브랜드가 아니다. |
AI 에이전트 생태계의 뜨거운 병목은 "모델은 똑똑한데 내 데이터·서비스에 닿기가 번거롭다"는 것이다. MCP(Model Context Protocol)가 그 통로를 표준화했지만, 여전히 툴마다 응답 모양이 제각각이라 에이전트가 여러 소스를 엮으려면 글루 코드가 필요하다. coral은 "모든 소스를 SQL 테이블로" 통일해, 에이전트가 데이터 모델 하나(관계형 테이블)와 언어 하나(SQL)만 알면 되게 만든다. "에이전트 시대의 데이터 접근 계층"이라는 서사가 트렌딩의 동력이다.
coral의 흥미로운 지점은 필터링·조인·집계처럼 답이 정해진 데이터 작업을 LLM이 아니라 검증된 쿼리 엔진(DataFusion)에 맡긴다는 점이다. 에이전트는 "무엇을 원하는지"를 SQL로 표현만 하고, "어떻게 효율적으로 가져올지"(어떤 필터를 API로 밀어 넣고, 어떤 순서로 조인할지)는 옵티마이저가 결정한다. LLM에게 수백 줄 JSON을 읽혀 헤매게 하는 대신, 정확하고 값싼 부품에 계산을 위임하는 태도다.
coral은 사실 MCP 서버이자, CLI이자, 로컬 gRPC 서버이자, 데스크톱 앱(Electron)이자, 웹 UI(React)다. 같은 러스트 엔진을 여러 진입점으로 노출한다 — 에이전트는 coral mcp-stdio로, 사람은 터미널이나 reef 데스크톱 앱으로. 9개 크레이트로 나뉜 러스트 워크스페이스 안에, DataFusion 커스텀 확장·gRPC·MCP 서버·봉투 암호화·OpenTelemetry까지 요즘 백엔드 인프라 기술이 밀집해 있어 학습 표본으로도 훌륭하다.
냉정히 보면 coral은 아직 v0.6.0의 빠르게 움직이는 초기 프로젝트다. 그리고 중요한 경계 — coral은 자연어를 SQL로 바꿔 주지 않는다(NL2SQL 내장 아님). SQL을 짜는 건 여전히 사람 또는 LLM의 몫이고, coral은 그 SQL을 안전하게(읽기 전용) 실행할 뿐이다. 또 소스를 쓰려면 먼저 YAML 스펙으로 등록해야 하며(번들 26개 + 커뮤니티 181개가 있지만 내가 쓸 API가 없으면 직접 작성), README의 벤치마크 수치(직접 MCP 대비 정확도·비용·지연 개선)는 제작사 자체 측정이라 참고로만 봐야 한다. 즉 "만능 데이터 마법사"가 아니라 "에이전트용 데이터 접근을 SQL로 재설계한, 잘 짜인 진행 중 인프라"로 보는 게 정확하다.
coral은 Rust로 짠 대규모 워크스페이스다(러스트 edition 2024, 빌드에 rustc 1.95 필요). 코드의 압도적 다수가 .rs(344개 파일, 154,259줄)이고, 프론트엔드(TypeScript/React)는 apps/ 아래 별도로 산다. 특기할 점 하나 — 워크스페이스 전역 린트가 unsafe_code = "forbid"다. unsafe 러스트를 아예 금지하고 unwrap()·인덱싱·panic까지 경고로 잡는, 안전성을 최우선으로 둔 코드베이스다. 스택을 ① 쿼리 엔진, ② 데이터 소스 접근, ③ 전송·서버, ④ 보안·자격증명, ⑤ 관측·CLI로 나눠 지도를 그려 보자.
| 영역 | 기술 · 버전 |
|---|---|
| SQL 엔진 코어 | datafusion 53 — SQL 파싱·논리계획·최적화·물리실행. coral의 모든 쿼리가 여기서 돈다 |
| SQL 파서 | sqlparser 0.59 — SQL 텍스트를 구문 트리로 |
| 인메모리 포맷 | arrow 58.1 — 엔진 내부 데이터가 흐르는 컬럼형 포맷. 결과는 Arrow IPC로 클라이언트에 전송 |
| 파일 포맷 | parquet 58.1 + datafusion-datasource — Parquet/CSV/JSON 읽기 |
| JSON 함수 | datafusion-functions-json — API 응답의 중첩 JSON을 SQL에서 다루기 |
| 구분 | 기술 · 버전 |
|---|---|
| 파일·오브젝트 스토어 | object_store 0.13(feature aws → S3) — 로컬 파일과 S3를 같은 인터페이스로. DataFusion ListingTable로 등록 |
| HTTP 클라이언트 | reqwest 0.13(rustls · http2 · blocking) — REST API 백엔드가 실제 API를 호출 |
| MCP 클라이언트 | rmcp 1.6(공식 Rust MCP SDK) — 다른 MCP 서버를 테이블로 감싸기 위해 coral이 MCP 클라이언트도 된다 |
| 소스 스펙 파싱 | serde_yaml + schemars 1.2 + jsonschema — 선언적 YAML 커넥터 정의를 파싱·검증 |
| 영역 | 기술 · 버전 |
|---|---|
| 내부 gRPC | tonic 0.14 + tonic-web · prost — 앱↔클라이언트 전송 프로토콜(coral-api 크레이트가 protobuf 계약) |
| REST/웹 | axum 0.8 + tower · tower-http(cors) — 로컬 HTTP 서버 |
| MCP 서버 | coral-mcp 크레이트 — coral mcp-stdio로 stdio(JSON-RPC) 표면을 에이전트에 노출 |
| 로컬 상태 DB | rusqlite(bundled) · sqlx 0.9(postgres+sqlite, rustls) · sea-query — 세션·스레드·워크스페이스 영속 |
데이터 소스 인증정보(API 키·토큰)를 다루는 만큼 보안이 두껍다. 저장 방식은 두 가지 — OS 키체인(keyring-core + macOS/Windows/libsecret 백엔드) 또는 AES-256-GCM 봉투 암호화 파일(ring 0.17로 암호화, zeroize로 메모리에서 즉시 소거). OAuth(device_code·PKCE)·헤더 인증·PAT를 지원하고, 자격증명은 환경변수나 대화형 프롬프트로만 주입되며 로컬을 떠나지 않는다.
관측성은 opentelemetry 0.31 풀스택 + tracing(W3C trace-context)으로 쿼리 실행을 추적한다. CLI는 clap 4.5(derive + env), dialoguer, crossterm. 프론트엔드는 apps/reef(React 19 + React Router 7 + react-aria)와 apps/desktop(Electron 42 + electron-vite, reef를 감싸고 coral 바이너리를 사이드카로 구동), 문서는 apps/docs(Mintlify)로 나뉜다.
| 크레이트 | 역할 |
|---|---|
| coral-engine | DataFusion 페더레이션 엔진 — 백엔드별 컴파일·런타임 등록·쿼리 실행. 가장 큰 크레이트(≈45K줄) |
| coral-app | 로컬 서버 조립, 상태·워크스페이스·소스 라이프사이클·자격증명·카탈로그 |
| coral-spec | 선언적 소스 스펙 파싱·검증·정규화(+ v4 DSL, OpenAPI import) |
| coral-api | protobuf 계약 + 생성된 러스트 바인딩(앱↔클라이언트 전송) |
| coral-mcp | MCP stdio 어댑터 — 에이전트가 마주하는 표면 |
| coral-cli | 터미널 어댑터(clap) |
| coral-client | 얇은 로컬 전송 부트스트랩 + Arrow IPC 디코드·렌더 |
| coral-telemetry | 크로스크레이트 트레이싱 헬퍼 |
| coral-auth-aws | AWS SigV4 등 인증 |
coral의 핵심 질문은 하나다 — "SQL over anything"을 대체 어떻게 구현하나?" 답은 이렇다. 각 데이터 소스를 선언적 YAML 스펙으로 기술하면, coral-spec이 검증하고, coral-engine이 백엔드(HTTP/파일/MCP)별로 DataFusion TableProvider로 컴파일해 카탈로그에 등록한다. 그 뒤 SessionContext에서 SQL을 실행하면, 엔진이 각 테이블의 provider를 호출해 실제 데이터를 끌어온다. 전체 지도를 먼저 그리자.
coral의 확장점은 3개 백엔드다(crates/coral-engine/src/backends/). 소스 스펙이 어떤 종류냐에 따라 RuntimeSourceComponent enum으로 갈라져, 각자 DataFusion에 등록될 테이블로 컴파일된다.
// crates/coral-engine/src/backends/mod.rs — 백엔드 디스패치
match component {
RuntimeSourceComponent::Http(manifest) => http::compile_manifest(manifest, request),
RuntimeSourceComponent::File(manifest) => file::compile_manifest(manifest, request),
RuntimeSourceComponent::Mcp(manifest) => mcp::compile_manifest(manifest, request),
}
| 백엔드 | 무엇을 테이블로 · 어떻게 |
|---|---|
| HTTP | REST API를 테이블로. HttpSourceTableProvider가 SQL의 WHERE/SELECT/LIMIT을 API 쿼리 파라미터로 밀어 넣는다(pushdown) |
| File | 로컬/S3 파일을 테이블로. DataFusion ListingTable + object_store. Parquet/JSONL/JSON/CSV 지원 |
| MCP | 다른 MCP 서버의 tool을 테이블로 감싼다. coral이 그 서버의 MCP 클라이언트가 되어 tool 호출을 행으로 |
가장 흥미로운 부분이다. SELECT title FROM github.issues WHERE owner='withcoral'이라는 SQL이, 실제로는 GET /repos/.../issues?owner=withcoral 같은 API 호출로 바뀐다. 이걸 predicate pushdown(술어 밀어넣기)이라 한다 — 필터 조건을 엔진이 로컬에서 거르는 대신 데이터 소스 쪽으로 밀어 보내 애초에 적게 가져오는 최적화다. DataFusion의 TableProvider 트레이트가 이 훅을 제공한다.
// backends/http/provider.rs — HttpSourceTableProvider: TableProvider
fn supports_filters_pushdown(&self, filters: &[&Expr])
-> Result<Vec<TableProviderFilterPushDown>>
{
// 이 API가 쿼리 파라미터로 받아 줄 수 있는 필터를 골라낸다
let active_request = self.table.resolve_request(&filter_value_keys);
let consumed = self.backend.request_filter_names(active_request);
Ok(classify_filter_pushdown_for_consumed(filters, self.table.filters(), &consumed))
}
async fn scan(&self, _state, projection, filters, limit)
-> Result<Arc<dyn ExecutionPlan>>
{
// 필수 필터가 없으면 에러(예: 검색 API에 q 누락)
// consumed 필터 → API 파라미터, 나머지(residual)는 DataFusion이 로컬 적용
http_json_exec(HttpJsonExecRequest { /* projection, limit, … */ })
}
즉 WHERE 절 중 API가 소화할 수 있는 조건은 파라미터로 pushdown되고, 나머지 조건은 결과를 받아 온 뒤 엔진이 로컬에서 마저 거른다(residual filter). SELECT한 컬럼만 projection으로 좁혀 응답을 줄인다. 이게 "API 트래픽·토큰 절감"의 실제 원리다.
새 API를 붙이는 데 러스트 코드를 짤 필요가 없다. YAML 한 장에 "이 API의 이 경로가 이 테이블이고, 이 필터를 받고, 응답의 이 JSON 경로가 이 컬럼"이라고 선언하면 커넥터가 된다(coral-spec이 검증). 아래는 실제 sources/core/github/manifest.yaml의 축약이다.
# sources/core/github/manifest.yaml (dsl_version 3, backend: http)
name: github
base_url: "{{input.GITHUB_API_BASE}}"
auth:
type: HeaderAuth
headers:
- { name: Authorization, template: "Bearer {{input.GITHUB_TOKEN}}" }
tables:
- name: accepted_assignments
filters: [{ name: assignment_id, required: true }]
request: { method: GET, path: /assignments/{{filter.assignment_id}}/accepted }
pagination: { mode: page, page_param: page, page_size: { query_param: per_page, max: 100 } }
columns:
- name: classroom_id
type: Int64
expr: { kind: path, path: [assignment, classroom, id] } # 중첩 JSON → 컬럼
functions:
- name: search_code # SQL 테이블 함수로 노출
request: { method: GET, path: /search/code }
여기서 배울 설계는 페이지네이션·인증·중첩 JSON 매핑을 전부 "데이터"로 기술했다는 점이다. pagination 블록을 엔진이 읽고 알아서 여러 페이지를 넘겨 가며 모으고, expr: {kind: path, ...}로 깊은 JSON을 평평한 컬럼으로 편다. functions:는 SELECT * FROM search_code(q => 'rust')처럼 SQL 테이블 함수가 된다. 커뮤니티가 이미 181개 소스 스펙을 기여했고(번들 코어 26개), OpenAPI 명세를 import해 자동 생성하는 경로(coral-spec/src/v4/surfaces/openapi/)도 있다.
coral에서 가장 고급인 부분이다. github.issues와 linear.tickets를 JOIN한다고 하자. 순진하게 하면 양쪽을 통째로 가져와 로컬에서 붙여야 한다. coral은 한쪽(resolver)에서 나온 값들을 다른 소스 API의 필터로 바인딩해 필요한 것만 호출하는 dependent join(의존 조인)을 구현했다. DataFusion의 확장 API 두 개를 직접 구현한다.
| 확장 | 역할 |
|---|---|
| DependentJoinNode | DataFusion의 UserDefinedLogicalNodeCore를 구현한 커스텀 논리 계획 노드 — "이 조인은 의존 조인이다"를 계획 트리에 표현 |
| DependentJoinOptimizerRule | DataFusion의 OptimizerRule — 조인을 분석해 "한쪽 값을 다른 API 필터로 밀어넣을 수 있으면" 의존 조인으로 변환 |
| BindingKey | 어떤 컬럼을 어떤 API 필터에 바인딩할지의 매핑. concurrency·max_bindings로 호출량 제어 |
주목할 만한 성숙함 — 이 규칙은 변환이 불리할 때의 폴백 사유를 taxonomy로 분류한다(NonEqui=등가조인 아님, NonHttpProvider, CostUnfavourable=비용상 불리, MissingRequired=필수 필터 없음 등). 즉 "무조건 pushdown"이 아니라 이득이 될 때만 하고, 아니면 일반 조인으로 되돌린다. 쿼리 옵티마이저를 직접 확장하는 실전 예제로서 학습 가치가 크다.
친구 20명의 "생일"을 A수첩에서 찾고, 그 생일에 해당하는 "그날의 뉴스"를 B서비스에서 가져와 붙인다고 하자. 순진한 방법은 B서비스의 모든 날짜 뉴스를 통째로 받아 A와 맞춰 보는 것 — 엄청난 낭비다. dependent join은 영리하다. 먼저 A수첩에서 20개의 생일(값)을 뽑고, 그 20개 날짜만 B서비스에 물어본다. "resolver(A)의 결과값을 dependent(B)의 필터로 바인딩"한다는 게 이 뜻이다. coral의 옵티마이저는 이 변환이 정말 이득일 때만(등가 조건이고, B가 그 필터를 받는 HTTP 소스이며, 비용이 유리할 때) 자동으로 적용한다.
coral은 read layer(읽기 계층)로 설계됐다. 쿼리 실행 경로(runtime/query.rs)에서 DataFusion의 SQLOptions로 DML/DDL(INSERT·UPDATE·DELETE·CREATE 등)을 원천 차단한다. 에이전트가 실수로(혹은 프롬프트 인젝션으로) 데이터를 바꾸려 해도 SQL 레벨에서 막힌다. 결과는 Arrow RecordBatch로 만들어져 Arrow IPC로 클라이언트에 스트리밍된다. "에이전트에게 데이터를 열되, 망가뜨릴 권한은 주지 않는다"는 원칙이 코드로 박혀 있다.
crates/가 두뇌(9 크레이트) · sources/가 커넥터 카탈로그 · apps/가 UI저장소를 열면 압도되기 쉽다(파일 1,647개, 러스트만 15만 줄). 하지만 뼈대는 단순하다 — crates/가 러스트 엔진 두뇌, sources/가 "어떤 API를 테이블로 쓸지"의 카탈로그, apps/가 사람이 쓰는 UI다. 엔진을 이해하려면 coral-engine부터, 커넥터를 이해하려면 sources/부터 보면 된다.
읽는 순서는 이렇다. ① README.md + AGENTS.md(크레이트 경계 규칙) → ② sources/core/github/manifest.yaml(소스가 어떻게 선언되는지) → ③ crates/coral-engine/src/backends/mod.rs(백엔드 디스패치) → ④ backends/http/provider.rs(SQL→API pushdown의 실제) → ⑤ runtime/dependent_join/(크로스 소스 JOIN) → ⑥ crates/coral-mcp/(에이전트가 마주하는 툴 8개). 엔진의 심장은 coral-engine, 커넥터의 문법은 sources/, 에이전트 접점은 coral-mcp — 이 세 곳만 훑어도 전체 그림이 잡힌다.
① sources/v4/는 프리뷰다. 차세대 소스 DSL이 들어와 있지만 현재 라이브 경로는 dsl_version 3(위 github 예제)다. 튜토리얼을 볼 때 v3/v4를 섞으면 헷갈린다 — 지금 쓰는 건 v3다. ② coral은 자연어→SQL 변환기가 아니다. "built for agents"라고 해서 coral이 SQL을 대신 짜 주는 게 아니라, SQL은 LLM(또는 사람)이 쓰고 coral은 실행·최적화만 한다. 에이전트가 좋은 SQL을 쓰도록 list_catalog·describe_table 같은 디스커버리 툴을 제공할 뿐이다. ③ 러스트 크레이트 이름(coral-*)과 프론트 앱 이름(reef)이 달라 "reef가 리브랜드?"로 오해하기 쉽지만, reef는 그냥 프론트엔드 서브앱이다(개명 아님).
coral이 학습 표본으로 훌륭한 이유는 DataFusion 확장, 데이터 페더레이션, 쿼리 옵티마이저 커스터마이징, 선언적 커넥터 DSL, MCP 서버, 봉투 암호화, 멀티크레이트 워크스페이스 설계가 한 저장소에 모여 있기 때문이다. 관심사별로 배울 것을 짚어 보자.
backends/http/provider.rs는 DataFusion TableProvider를 직접 구현하는 실전 레퍼런스다. "데이터를 어디서 어떻게 읽어올지"를 엔진에 꽂는 법, supports_filters_pushdown/scan으로 술어·투영·리밋 pushdown을 설계하는 법을 볼 수 있다. "쿼리 엔진을 통째로 만들지 않고, 검증된 엔진에 내 데이터 소스만 얹는" 현대적 접근의 교재다.
runtime/dependent_join/는 커스텀 OptimizerRule + UserDefinedLogicalNodeCore로 쿼리 계획을 재작성하는 드문 실전 예제다. "언제 이 최적화가 이득인가"를 비용 기반으로 판단하고, 불리하면 폴백하는 taxonomy 설계까지 담겨 있다. 데이터베이스 내부(옵티마이저)가 어떻게 동작하는지 코드로 배우고 싶은 사람에게 최고의 자료다.
sources/ + coral-spec은 API 통합을 코드가 아니라 데이터(YAML)로 기술하는 법을 보여 준다. 페이지네이션·인증·중첩 JSON 매핑·테이블 함수를 선언으로 표현하고, OpenAPI 명세를 import해 커넥터를 자동 생성하는 파이프라인(coral-spec/src/v4/surfaces/openapi/)까지. "N개 API를 어떻게 확장 가능하게 통합하나"의 아키텍처 답안이다.
coral-mcp는 rmcp(공식 러스트 MCP SDK)로 서버를 짜는 레퍼런스다. 노출 툴이 흥미롭다 — sql(읽기 전용 쿼리 배치), search, list_catalog·describe_table·list_columns(디스커버리), start_task/end_task, feedback. "에이전트가 스스로 스키마를 탐색해 좋은 SQL을 쓰도록" 유도하는 discovery-first 워크플로를 배운다.
9개 크레이트로 책임을 가른 구조(엔진/앱/스펙/전송/MCP/CLI…)와 AGENTS.md에 명문화된 크레이트 소유권 규칙은 대규모 러스트 프로젝트의 경계 설계 교재다. 여기에 unsafe_code = "forbid" 정책, AES-256-GCM 봉투 암호화 자격증명(ring+zeroize), OS 키체인 연동까지 — 실무 보안 패턴이 함께 담겨 있다.
sources/core의 소스 하나(예: github)를 붙인 뒤 SELECT 한 줄로 API를 SQL로 질의해 본다. ② list_catalog·describe_table로 등록된 테이블·컬럼을 탐색하며 "에이전트가 스키마를 어떻게 발견하는지" 체험한다. ③ sources/community의 YAML 스펙 하나를 열어 pagination·auth·columns 매핑이 어떻게 선언됐는지 읽는다. 이 세 가지만 해 봐도 "이 도구가 안에서 뭘 하는지" 손에 잡힌다.coral은 클라우드 인프라가 필요 없다. 실행 파일 하나로 도는 로컬 도구이고, 무거운 모델을 돌리지 않으므로 특별한 GPU나 고사양이 필요 없다. 소스에서 빌드하려면 러스트 툴체인이, 그냥 쓰려면 프리빌트 바이너리만 있으면 된다.
| 항목 | 요구/설명 |
|---|---|
| 빌드 툴체인 | Rust 1.95(edition 2024) — 소스 빌드 시. 프리빌트를 쓰면 불필요 |
| 실행 | 단일 바이너리 — macOS brew install withcoral/tap/coral, Linux curl … install.sh, Windows 10/11 x86_64 zip |
| OS | macOS · Linux · Windows |
| 런타임 의존 | 없음(단일 바이너리). Postgres 소스를 테스트할 때만 Docker 사용 |
| 자격증명 | 데이터 소스 키/토큰은 환경변수 또는 대화형 프롬프트로 주입 → OS 키체인 또는 봉투암호화 파일에 보관 |
| 네트워크 | API·라이브 소스 질의 시 실시간 접근 필요(파일 소스는 로컬만으로 가능) |
# 설치 (macOS 예시)
brew install withcoral/tap/coral
# MCP 서버로 에이전트에 연결 (Claude Code 예시)
claude mcp add coral -- coral mcp-stdio
# Codex · Cursor 등 MCP 지원 에이전트에 동일 방식으로 등록
# 에이전트용 스킬 설치 (범용)
npx skills add withcoral/skills
# 소스(커넥터) 추가 후 SQL로 질의 — SQL은 사람/LLM이 작성
# 예) SELECT number, title FROM github.issues WHERE state = 'open' LIMIT 20
① 소스를 먼저 등록해야 한다 — coral은 아무 API나 즉시 읽지 않는다. sources/core(26개)·community(181개)에 있으면 붙이면 되지만, 내가 쓸 API가 없으면 YAML 스펙을 직접 작성해야 한다. ② SQL은 내가(또는 LLM이) 쓴다 — coral에 NL2SQL은 없다. 자연어로 질의하려면 앞단의 LLM이 SQL을 생성해 줘야 한다. ③ 읽기 전용이다 — 데이터를 바꾸는 용도가 아니다(설계상 DML/DDL 차단). ④ 초기 버전 — v0.6.0으로 빠르게 바뀌는 중이니, 버전·소스 스펙 문법은 클론한 소스 기준으로 확인하는 게 안전하다.
coral를 설치하고 sources/core의 소스 하나(예: github)를 붙인 뒤, SELECT number, title FROM github.issues WHERE state='open' LIMIT 10 같은 SQL을 실행한다. 이 SQL이 실제로는 API 호출로 바뀌어 정돈된 테이블로 돌아오는 걸 관찰한다. 목표: "데이터 페더레이션 = 소스를 옮기지 않고 제자리에서 질의"를 체감.
list_catalog → describe_table → list_columns 순서로 등록된 테이블·컬럼을 탐색한다. 그런 다음 같은 흐름을 Claude Code에 coral을 붙여(claude mcp add coral -- coral mcp-stdio) 에이전트가 스스로 스키마를 발견해 SQL을 쓰는 과정을 지켜본다. 목표: discovery-first 워크플로가 왜 필요한지 이해.
sources/core/github/manifest.yaml을 본떠, 키가 필요 없는 공개 REST API(예: 공공데이터·오픈 API) 하나를 테이블로 선언하는 manifest.yaml을 쓴다. request.path·pagination·columns[].expr(path)로 응답 JSON을 컬럼에 매핑하면 된다. 목표: "코드 없이 데이터로 커넥터를 만드는" 선언적 통합을 체득.
coral 없이, DataFusion을 직접 의존성에 넣고 고정 행을 반환하는 초소형 TableProvider를 러스트로 짠다. scan()이 Arc<dyn ExecutionPlan>을 돌려주는 인터페이스, supports_filters_pushdown()으로 필터를 소비하는 흐름을 손으로 만들어 본다. coral의 backends/http/provider.rs가 참고 예시. 목표: "쿼리 엔진에 내 데이터 소스를 꽂는" 확장점의 원리를 코드로 증명.
두 소스(예: github + 로컬 CSV)를 등록하고 JOIN 쿼리를 던진 뒤, 한쪽 값이 다른 소스의 필터로 밀려 들어가는지(dependent join) 실행 계획을 살펴본다. 필터가 pushdown되지 않는 조건(비등가 조인 등)도 만들어 폴백을 관찰한다. coral의 runtime/dependent_join/가 참고. 목표: 쿼리 옵티마이저가 "언제 최적화하고 언제 포기하는지"를 체득.
| 주차 | 주제 · 학습 목표 |
|---|---|
| 1주차 SQL · DataFusion 기초 | 관계형 모델과 쿼리 엔진. SQL의 기본(SELECT·WHERE·JOIN), 관계형 사고, 그리고 Apache DataFusion의 구조 — TableProvider·LogicalPlan·ExecutionPlan·Arrow 컬럼 포맷. 작은 CSV를 DataFusion으로 질의해 보며 "엔진이 데이터를 어떻게 흐르게 하는지" 익힌다. |
| 2주차 데이터 페더레이션 · 커넥터 | 소스를 옮기지 않고 합치기. predicate/projection pushdown의 원리, 선언적 커넥터 DSL, 페이지네이션·인증을 데이터로 기술하기, OpenAPI import. coral sources/·backends/http/를 교재로. Steampipe·Trino와 비교하며 "페더레이션 vs ETL"을 정리. |
| 3주차 MCP · 에이전트 통합 | 에이전트에게 데이터를 주는 법. Model Context Protocol, rmcp로 MCP 서버 짜기, discovery-first 툴 설계(list_catalog/describe_table), 읽기 전용 강제. "왜 SQL 인터페이스가 툴 호출 폭증을 줄이는가"를 벤치마크 관점에서 분석. |
| 4주차 쿼리 옵티마이저 · 보안 | 엔진 내부와 실무 보안. DataFusion OptimizerRule·UserDefinedLogicalNode로 계획 재작성(dependent join), 비용 기반 판단·폴백. 나아가 AES-256-GCM 봉투 암호화·OS 키체인·멀티크레이트 워크스페이스 경계 설계까지 — 대규모 러스트 인프라의 정석. |
TableProvider로 꽂게 열어 둔다. coral의 심장.WHERE)와 선택 컬럼(SELECT)을 엔진이 로컬에서 거르는 대신 데이터 소스 쪽으로 밀어 보내 애초에 적게 가져오는 최적화. coral이 SQL을 API 쿼리 파라미터로 번역하는 원리.DependentJoinOptimizerRule·DependentJoinNode가 이걸로 만들어졌다.manifest.yaml). base_url·auth·tables·pagination·columns 매핑을 선언하면 커넥터가 된다. 현재 라이브는 dsl_version 3(v4는 프리뷰).object_store가 로컬 파일과 S3를 같은 인터페이스로 열고, DataFusion ListingTable이 Parquet/JSONL/JSON/CSV를 테이블로 등록한다.rmcp SDK로 만든 MCP 서버가 stdio로 툴을 노출한다. 핵심 툴 = sql(읽기 전용 쿼리)·search·디스커버리 3종·start_task/end_task·feedback(총 8개).list_catalog·describe_table·list_columns로 먼저 스키마를 발견한 뒤 SQL을 쓰게 하는 설계. "테이블·컬럼을 모른 채 SQL을 헛짜는" 것을 막는다.SQLOptions로 DML/DDL(INSERT·UPDATE·CREATE 등)을 SQL 레벨에서 차단해, 에이전트가 실수나 인젝션으로 데이터를 바꾸지 못하게 한다.ring), 쓴 뒤 메모리에서 즉시 소거(zeroize). OS 키체인 저장도 선택 가능. 키는 로컬을 떠나지 않는다.| 도구 | coral과의 관계 |
|---|---|
| Steampipe | 가장 유사(SQL over API). 단 Postgres FDW 기반 vs coral은 DataFusion + 로컬 + MCP + 에이전트 특화 |
| Trino / Presto | 분산 대규모 페더레이션 쿼리 엔진. coral은 그 반대편의 경량 local-first |
| Apache DataFusion | coral이 올라탄 토대 라이브러리(경쟁 아님) |
| DuckDB | 임베디드 분석 DB. coral은 저장이 아니라 라이브 API 페더레이션에 초점 |
저장소 · 제품
· GitHub: github.com/withcoral/coral (Apache-2.0 · v0.6.0 · 최신 커밋 2026-07-17, PR #1799)
· 조직/제품: withcoral.com
· 토대: Apache DataFusion · 가장 가까운 대안: Steampipe
핵심 파일(클론 후)
· crates/coral-engine/src/backends/mod.rs — 3개 백엔드 디스패치
· crates/coral-engine/src/backends/http/provider.rs — SQL→API pushdown(TableProvider)
· crates/coral-engine/src/runtime/dependent_join/ — 크로스 소스 JOIN 옵티마이저
· crates/coral-engine/src/runtime/query.rs — SessionContext·읽기 전용 강제
· crates/coral-mcp/src/ — MCP 서버(에이전트 표면, 툴 8개)
· sources/core/github/manifest.yaml — 선언적 소스 스펙 예시
배경 지식
· Model Context Protocol — 에이전트 툴 표준
· Apache Arrow · DataFusion · sqlparser-rs
· Federated database system — 페더레이션의 개념
· object_store — 로컬/S3 파일 접근
① 새 DB 아님 — 데이터를 저장하는 게 아니라, 흩어진 소스를 제자리에서 SQL로 질의하는 페더레이션 엔진이다. ② NL2SQL 없음 — SQL은 사람/LLM이 쓰고 coral은 실행만 한다("built for agents"의 실제 의미는 MCP 표면 + 디스커버리 툴 제공). ③ 소스 등록 선행 — 쓰려는 API가 sources/에 없으면 YAML 스펙을 직접 작성해야 한다. ④ 읽기 전용 — DML/DDL은 SQL 레벨에서 차단(데이터 변경 도구 아님). ⑤ v4 DSL은 프리뷰 — 라이브 경로는 dsl_version 3. ⑥ reef는 개명 아님 — apps/reef는 프론트엔드 서브앱일 뿐, coral의 리브랜드가 아니다(크레이트는 전부 coral-*). README 벤치마크 수치는 제작사 자체 측정이니 참고로만.