flint-chart/flint-chart-mcp v0.2.2(2026-07-15) · 최신 커밋 2026-07-17 · 별 1,854 · 이번 회차는 TrendShift Daily·Weekly·Monthly가 모두 기분석 소진되어 OSSInsight 순위로 폴백)
microsoft/flint-chart(줄여서 Flint)는 차트를 "어떻게 그릴지" 일일이 지정하는 대신, "데이터가 무엇을 뜻하는지"와 "어떤 차트인지"만 짧게 선언하면 되도록 만든 오픈소스 시각화 도구다. 저장소가 내건 한 줄은 이것 — "Flint is a visualization language that lets AI agents reliably create expressive, good-looking charts from simple, human-editable chart specs."(AI 에이전트가 간단하고 사람이 편집 가능한 차트 스펙으로부터 표현력 있고 보기 좋은 차트를 안정적으로 만들게 해 주는 시각화 언어).
핵심은 두 개의 선언을 나눠 준다는 것이다. ① semantic_types — 각 데이터 열이 무슨 의미인지(예: price는 "금액", 202401은 "연월", rank는 "순위"). ② chart_spec — 어떤 차트 타입에 어떤 열을 어느 채널(x·y·color·size…)에 꽂을지. 대개 10줄 안팎이다. 그러면 Flint 컴파일러가 스케일·축·0 기준선·집계 방식·통화/퍼센트/날짜 포맷·색상 스킴·범례·여백·facet 격자·종횡비까지 전부 데이터 + 의미 + 차트타입으로부터 유도해, Vega-Lite / ECharts / Chart.js 각각의 완성된 네이티브 스펙을 뽑아낸다.
202401을 그냥 큰 숫자로 그려 버린다. Flint은 여기에 의미 타입 레이어를 얹는다 — YearMonth(연월)라고 말해 주면 시간 축으로, Price면 통화 포맷 + 0 기준선으로, Correlation이면 [-1, 1] 고정 발산형 색상으로, Rank면 축을 뒤집어(1위가 위로) 그린다. "의미"가 디자인을 결정한다.직접 Vega-Lite/ECharts 스펙을 쓰는 건 요리의 불 세기·간·플레이팅을 한 줄씩 다 지시하는 것과 같다. 한 번은 되지만, 재료(필드)를 바꾸거나 접시(차트 타입)를 바꾸면 지시서 전체가 무너진다.
Flint에게는 "이건 가격, 이건 연월, 이건 순위"라고 재료의 성격만 말하면 된다. 주방장(컴파일러)이 "가격이면 원화 표기에 0부터", "순위면 1등을 맨 위로" 같은 요리 규칙을 스스로 적용해 완성된 접시를 내온다. 게다가 같은 주문서로 양식·중식·일식(Vega-Lite·ECharts·Chart.js) 어느 주방에서든 똑같이 차려 준다.
Flint이 2개월 만에 별 1,800+를 모으며 트렌딩에 오른 이유는 "또 하나의 차트 라이브러리"여서가 아니다. 기존 선언형 문법(Vega-Lite·ECharts)이 의미와 저장표현이 어긋날 때 취약하다는 오래된 문제와, LLM 에이전트가 장황한 차트 스펙을 안정적으로 못 만든다는 새 문제를 동시에 겨냥했기 때문이다. 제작자는 Microsoft Research와 런민대(Renmin University) IDEAS Lab이며, 논문도 "coming soon"으로 예고돼 있다.
| 포인트 | 내용 |
|---|---|
| 정체 | 시맨틱 기반 시각화 컴파일러. data + semantic_types + chart_spec을 받아 Vega-Lite·ECharts·Chart.js 네이티브 스펙을 생성. 30+ 통계 차트 타입, 70개 안팎의 시맨틱 타입. |
| 차별점 ① | 시맨틱 타입이 1급. 저장 타입(Q/O/N/T)이 아니라 의미(Price·YearMonth·Rank·Correlation…)가 포맷·색상·축 방향을 결정. 3계층 graceful degradation(뒤에서 상술). |
| 차별점 ② | 물리 기반 auto-layout. 데이터가 캔버스보다 커지면 스프링·가스 압력·원주·면적 모델로 크기를 조절 — 문서에 실제 수식까지(1,110줄). |
| 차별점 ③ | 군론적 Named Views. 축 전치·색↔계열 교환·facet·형제 차트로의 전환을 4개 생성원의 궤도(orbit) + 안정자 몫(stabilizer quotient)으로 열거·중복제거. 추상대수의 실전 적용. |
| 차별점 ④ | 에이전트 통합이 기본. 별도 npm 패키지 flint-chart-mcp(MCP 서버) + 566줄짜리 에이전트 스킬을 제공해, 어떤 MCP 에이전트든 신뢰성 있는 차트 작가로 만든다. |
| 제작자 · 라이선스 | Microsoft Research × 런민대 IDEAS Lab. 공식 microsoft/ 저장소. MIT 라이선스. 코어 라이브러리는 런타임 의존성 0개. |
Vega-Lite·ECharts 같은 문법은 저장 타입이 시각 매핑과 맞아떨어질 때는 훌륭하다. 문제는 의미가 저장표현과 어긋날 때 생긴다. 저장소 문서가 든 대표 실패 사례 세 가지 — ① 정수 202001은 사실 연월(시간 밴드)인데 크기 값으로 그려진다. ② 온도·비율처럼 더할 수 없는(non-additive) 측정값을 문법은 아무렇지 않게 스태킹해 버린다. ③ 손익·상관계수처럼 발산형(diverging) 필드를 순차 색상 램프에 잘못 매핑한다. 전문가는 긴 스펙을 손수 짜서 고칠 수 있지만, 필드 하나만 바꿔도 그 스펙이 통째로 깨진다. Flint은 "의미"를 1급으로 올려 이 취약성을 컴파일러가 책임지게 했다.
에이전트가 차트를 그리려고 완전한 Vega-Lite 스펙을 통째로 생성하면 토큰이 비싸고, 작은 수정에도 스펙 전체를 다시 뱉어야 하며, 틀리기 쉽다. Flint의 접근은 정반대다 — 에이전트는 컴팩트하고 안정적인 프로그램(짧은 스펙)만 내보내고, "정확성"은 컴파일러가 맡는다. 그래서 MCP 서버·에이전트 스킬이 부록이 아니라 설계의 중심에 있다. "AI가 데이터로 차트를 그리는" 워크플로가 폭증하는 지금, 정확히 그 지점의 신뢰성 도구라는 점이 트렌딩의 동력이다.
vs Vega-Lite/ECharts/Chart.js 직접 작성 — 그것들은 Flint의 타깃(출력)이다. 10줄을 쓰면 컴파일러가 수백 줄의 네이티브 스펙을 유도한다.
vs matplotlib / 에이전트가 직접 짠 플로팅 코드 — 장황하고 취약하며 매번 재실행이 필요. Flint 스펙은 짧고 선언적이며 백엔드 간 이식 가능.
vs Mermaid — Mermaid는 다이어그램(플로차트·시퀀스) 중심에 고정 스타일. Flint은 데이터 시각화 중심(30+ 통계 차트) + 의미 기반 자동 디자인.
vs Vega-Lite의 "스마트 기본값" — Vega-Lite 기본값은 저장 타입 기반. Flint은 그 위에 70개 시맨틱 레이어를 더해 통화 포맷·발산 색상·순위 역축처럼 Vega-Lite가 추론 못 하는 디자인 결정을 내린다.
Flint은 npm workspaces 모노레포다("name": "flint-chart-monorepo"). 네 개의 워크스페이스로 나뉘고, 엔진 요구사항은 Node ≥ 18이다. 흥미로운 설계 결정 하나 — 코어 라이브러리는 런타임 의존성이 0개이고, 실제 렌더러(Vega·ECharts·Chart.js)는 전부 선택적 peer dependency다. 즉 코어는 순수 TypeScript로 "스펙 객체"만 뱉고, 무거운 렌더링 라이브러리는 그걸 쓰는 쪽이 골라 설치한다.
| 워크스페이스 | 배포 이름 · 역할 |
|---|---|
| packages/flint-js | npm flint-chart v0.2.2 — 코어 라이브러리(약 58,700줄). 시맨틱 해석 + 레이아웃 + 3개 백엔드 코드생성. TypeScript ESM+CJS. |
| packages/flint-mcp | npm flint-chart-mcp v0.2.2 — MCP 서버(약 3,800줄). 에이전트에게 차트 렌더/컴파일/검증 도구를 노출, 서버측 PNG 래스터화 포함. |
| packages/flint-py | PyPI flint-chart v0.1.0 — Python 포팅 프리뷰(약 13,000줄). 코어의 1:1 이식이나 현재 Vega-Lite 백엔드만. (미출시) |
| site/ | 데모 사이트 — Vite + React + TS. 갤러리 · 라이브 에디터(CodeMirror) · 문서. 개발 서버 포트 5274. |
flint-chart (packages/flint-js)언어는 TypeScript ^5.6, ESM 우선("type": "module", "sideEffects": false)에 CJS/ESM 이중 출력. 빌드는 tsup ^8.3, 테스트는 vitest ^4.1, 린트는 eslint ^9 + typescript-eslint ^8. 서브패스 export(./core, ./vegalite, ./echarts, ./chartjs, ./test-data, ./gallery)로 필요한 부분만 가져온다.
// packages/flint-js — peerDependencies (전부 optional: 쓰는 렌더러만 설치)
"peerDependencies": {
"chart.js": "^4.0.0",
"echarts": "^5.0.0 || ^6.0.0",
"vega": "^5.0.0 || ^6.0.0",
"vega-lite": "^5.0.0 || ^6.0.0"
}
// ★ 코어 자체의 runtime dependencies = 0개 (순수 TS가 plain 객체를 반환)
flint-chart-mcp (packages/flint-mcp)바이너리 flint-chart-mcp(→ dist/cli.js)로 실행한다. 여기는 렌더러들을 실제 의존성으로 품는다 — 서버가 직접 PNG/SVG를 그려 에이전트에 돌려줘야 하기 때문이다.
| 의존성 | 역할 |
|---|---|
| @modelcontextprotocol/sdk ^1.29 @modelcontextprotocol/ext-apps ^1.7 | MCP 프로토콜 SDK + MCP App UI(대화형 차트 뷰) 확장 |
| vega ^6 · vega-lite ^6 vega-interpreter ^2.2 | Vega 런타임. vega-interpreter는 CSP-safe(eval 없이) 실행 — 보안 샌드박스용 |
| echarts ^6 · chart.js ^4.4 | 나머지 두 백엔드 렌더러 |
| @napi-rs/canvas · @resvg/resvg-js | 서버측 래스터화 — SVG→PNG, 캔버스 기반 PNG 출력 |
| zod ^3.25 | MCP 도구 입력 스키마 검증(문법의 검증 표면) |
| (devDeps) react 19 · vite 8 vite-plugin-singlefile | 대화형 "chart view" MCP App UI를 React 단일 파일 번들로 빌드 |
flint-py · 데모 사이트flint-py는 코어의 near 1:1 이식(파이썬 ≥ 3.10, 유일 런타임 의존성 python-dateutil). 도구체인은 uv(락파일) + ruff + pytest. JS 구현과의 "갤러리 패리티(parity)"를 자동 평가하는 하니스(tools/run_full_eval.py)까지 있어, 두 구현이 같은 결과를 내는지 공유 픽스처로 검증한다. 데모 site/는 Vega-Lite/ECharts/Chart.js 세 뷰 컴포넌트 + JSON 라이브 에디터로, flint-chart를 소스(packages/flint-js/src)로 alias 걸어 라이브러리 수정이 바로 hot-reload 된다.
Flint은 스스로를 컴파일러로 명시한다. 공유되는 프론트엔드 + 옵티마이저가 있고, 백엔드마다 코드 생성기가 따로 있다. 핵심 통찰은 1·2단계(시맨틱 + 레이아웃)는 100% 백엔드 독립이고, 오직 3단계만 백엔드별로 다르다는 것. 그래서 새 렌더링 백엔드(예: Plotly)를 추가하려면 3단계만 구현하면 된다.
이 파이프라인의 중심 오케스트레이션은 packages/flint-js/src/vegalite/assemble.ts(1,152줄)에 있다. 아래는 이 컴파일러를 떠받치는 세 가지 핵심 설계 패턴이다.
모든 chartType 이름은 하나의 ChartTemplateDef에 매핑된다. 백엔드마다 레지스트리(vlTemplateDefs 등)가 있고 이름으로 조회한다. 30개가 넘는 차트가 전부 같은 인터페이스를 구현하되, 생명주기 훅(declareLayoutMode → normalizeEncodings → instantiate → postProcess)으로 개별 동작을 끼워 넣는 플러그인 아키텍처다.
// core/types.ts — 모든 차트 타입이 구현하는 계약
interface ChartTemplateDef {
chart: string; // "Bar Chart" — chart_spec.chartType과 일치
template: any; // 네이티브 스펙 골격 { mark:"bar", encoding:{} }
channels: string[]; // 허용 인코딩 채널
markCognitiveChannel: 'position'|'length'|'area'|'color';
// 마크가 크기를 어떻게 표현하나 → 0기준선·stretch 클래스 결정
declareLayoutMode?: (cs, table) => LayoutDeclaration; // 어느 축이 밴드형?
instantiate: (spec, ctx) => void; // 최종 스펙 조립
properties?: ChartPropertyDef[]; // 조절 노브(예: cornerRadius)
pivot?: PivotDef; // named-view 변환(패턴 ③)
}
// vegalite/templates/bar.ts — 패턴 전체가 ~30줄에 담긴다
export const barChartDef: ChartTemplateDef = {
chart: "Bar Chart",
template: { mark: "bar", encoding: {} },
channels: ["x","y","color","group","opacity","column","row"],
markCognitiveChannel: 'length', // 막대는 "길이"로 크기 표현
declareLayoutMode: (cs, table) => {
const r = detectBandedAxisFromSemantics(cs, table, { preferAxis: 'x' });
return { axisFlags: r ? { [r.axis]: { banded: true } } : { x: { banded: true } } };
},
instantiate: (spec, ctx) => {
defaultBuildEncodings(spec, ctx.resolvedEncodings);
if (ctx.chartProperties?.cornerRadius > 0)
spec.mark = setMarkProp(spec.mark, 'cornerRadius', ctx.chartProperties.cornerRadius);
adjustBarMarks(spec, ctx);
},
encodingActions: [makeSortAction()],
pivot: makeCartesianPivot({ transpose: [['x','y']], permute: [['x','y','color']] }),
};
옵티마이저는 두 숫자에서 출발한다 — baseSize(차트가 지향하는 목표 크기, 기본 400×320)와 canvasSize(절대 넘으면 안 되는 상한, 기본 = baseSize × maxStretch, maxStretch 기본 1.5×). 데이터가 늘면 기하 형태별로 다른 물리 모델로 크기를 키운다.
| 모델 | 기하 · 대상 차트 | 아이디어 |
|---|---|---|
| Elastic Budget | 1D 밴드 축 · 막대·히스토그램·히트맵·박스플롯 | 안식 길이 L₀를 가진 스프링. 범주가 늘면 넓어지고, 최소 가독 스텝까지 압축되다, 한계 넘으면 overflow |
| Gas Pressure | 2D 점 구름 · 산점도·라인·면적 | 점 밀도에 따라 축이 가스 압력(수요÷공급)처럼 부드럽게 늘어남 |
| Circumference | 1D 닫힌 고리 · 파이·로즈·선버스트·레이더·게이지 | 슬라이스가 붐비면 반지름을 키워 원주 가독성 유지 |
| Area (2D) | 2D 채움 · 트리맵 | 모든 사각형이 읽히도록 캔버스 면적을 키움 |
데이터가 그래도 안 들어가면 overflow 필터가 항목을 버리고 ChartWarning 메타데이터를 붙인다 — 읽을 수 없는 차트를 그리느니 경고와 함께 잘라낸다. 이 "물리를 레이아웃 휴리스틱으로" 쓰는 발상은 "제한된 공간에 콘텐츠 맞추기"라는 온갖 문제에 전이 가능한 아이디어다(라인 차트의 banking-to-45° 종횡비 최적화도 포함).
차트 변형(축 뒤집기, 계열↔색상 교환, facet, 형제 차트로 전환)을 매번 스펙 재작성으로 얻는 대신, Flint은 이를 4개 생성원(generator)이 만드는 궤도로 모델링한다. 원저작 배치 a0에서 시작해 아래 연산을 조합해 걷는다.
| 기호 | 생성원 | 의미 |
|---|---|---|
| τ (tau) | transpose | 두 축 슬롯을 통째로 전치(flip:x-y) |
| σ (sigma) | permute | 같은 프로파일 채널끼리 교환(swap:y-color, 측정값↔측정값) |
| γ (gamma) | shift | 이산 계열 필드를 color/group/facet 채널 사이로 라우팅 |
| θ (theta) | transition | 같은 필드를 형제 템플릿으로 다시 렌더(type:Strip Plot) |
중복 제거는 안정자 몫(stabilizer quotient)을 코드로 구현한 것이다 — flip을 두 번 하면 항등(identity), facet 후 jitter가 Strip Plot로 접히는 등 같은 상태로 수렴하는 변형을 자동 병합한다. 호환성은 타입으로 강제된다(σ는 같은 프로파일만, τ는 슬롯을 바꾸므로 프로파일을 넘나들 수 있음). 라인 차트는 τ를 생략해 "세로 라인 차트" 같은 무의미한 변형을 아예 제안하지 않는다. 구현은 core/pivot.ts. 추상대수(궤도·생성원·안정자)를 실전 UI 상태 열거에 쓴, 보기 드문 사례다.
data + semantic_types + chart_spec(+ options)로 이뤄진다. data는 인라인 행({values:[...]})이나 로컬 파일 경로({url:"..."}). chart_spec 안에 chartType·encodings·선택적 baseSize/canvasSize/chartProperties가 들어간다. 인코딩에서 맨 문자열은 {field}의 축약이다(예: y: "mpg" = y: {field:"mpg"}).core/클론 직후의 최상위 구조와, 코어의 내부다. HEAD 커밋은 8e8d1d2, 전체 2,587 파일에 TS/Py 실코드 약 86,600줄이다.
core/가 이 프로젝트의 지적 핵심이다(백엔드 독립 1·2단계). 파일 크기순으로 무엇이 있는지 보면 "이 컴파일러가 진짜로 무슨 일을 하는지"가 드러난다.
| 파일(core/) | 크기 | 역할 |
|---|---|---|
| compute-layout.ts | 78KB | 레이아웃 엔진 — elastic budget, gas pressure, 원주, 면적, facet 격자 |
| types.ts | 50KB | 모든 코어 타입(ChartAssemblyInput, ChartTemplateDef, ChannelSemantics…) |
| recommendation.ts | 49KB | 채널 역할 추천·적응(adaptChannels / recommendChannels) |
| semantic-types.ts | 42KB | 시맨틱 타입 카탈로그 + 격자(lattice) + 0결정·색상·범주 추론 |
| field-semantics.ts | 42KB | 필드별 해석: 포맷·집계·도메인·눈금·발산·정규 정렬 |
| decisions.ts | 38KB | 재사용 결정 함수: computeElasticBudget, computeGasPressure, computeOverflow… |
| pivot.ts | 26KB | named-view / pivot 열거 + 합성(군의 궤도) |
| type-registry.ts | 15KB | 단일 진실원 TYPE_REGISTRY: 타입→시각 범주+기본값 |
| static-series.ts | 10KB | wide→long "static-series fold"(배열 인코딩) |
README.md로 전체 그림과 갤러리를 잡고 → ② docs/overview.md(동기·dataSpec/chartSpec 분리) → ③ docs/architecture.md(3단계 + named-views) → ④ core/types.ts에서 계약을 확인 → ⑤ vegalite/templates/bar.ts 같은 가장 단순한 템플릿 하나를 통독하면 "짧은 스펙이 네이티브 스펙으로 변하는" 전 과정이 손에 잡힌다. 백엔드는 하나(vegalite)만 깊게 보고 나머지는 차이만 훑는 게 효율적이다.Flint이 학습 표본으로 훌륭한 이유는 컴파일러 아키텍처, 계층형 타입 시스템, 물리 휴리스틱, 군론 응용, 플러그인 패턴, MCP 서버 엔지니어링, 에이전트 프롬프트 설계, 크로스언어 포팅이 한 저장소에 모여 있기 때문이다. 관심사별로 짚어 보자.
프론트엔드/옵티마이저/코드생성 분리, "중간 표현(IR)" 프레이밍, "하나의 입력 → 여러 타깃"은 교과서적 컴파일러 구조를 차트라는 비-프로그래밍 도메인에 적용한 것이다. core/는 타깃 독립, vegalite|echarts|chartjs/는 백엔드 코드 생성기. "PL이 아닌 대상을 위한 컴파일러를 어떻게 짜는가"를 배우기 딱 좋다.
Flint의 "언어"는 JSON 모양(ChartAssemblyInput)이라 렉서/파서 대신 정규화 + 축약 확장(맨 문자열 → {field})과 zod 스키마(MCP 계층)를 문법의 검증 표면으로 쓴다. "DSL이 꼭 자체 파서를 가질 필요는 없다 — 임베디드/JSON DSL도 엄밀한 타이핑이 가능하다"는 교훈.
type-registry.ts·semantic-types.ts의 3계층 격자(Family → Category → Specific)는, 거친 라벨(Measure)만 줘도 "일반적이지만 올바른" 결과를 내고 구체 라벨(Price)을 주면 완전한 포맷팅을 푼다. 불완전한/LLM 입력을 견디도록 설계하는 아름다운 예다. (README의 "70+ 타입" = Family 6 + Category 17 + Specific 46 ≈ 69개.)
decisions.ts + compute-layout.ts + design-stretch-model.md. 축 크기를 스프링(elastic budget)·가스 압력으로 모델링하는 건 "제한 공간에 콘텐츠 맞추기"라는 온갖 문제에 전이 가능한 발상이다(라인 차트 banking-to-45° 종횡비 최적화 포함).
pivot.ts의 궤도·생성원·안정자 몫으로 UI 상태를 열거·중복제거하는 건 추상대수의 드문 실전 응용이다. "이론이 코드가 되는" 감을 잡기에 최고의 사례.
30+ 템플릿이 같은 ChartTemplateDef 인터페이스를 생명주기 훅으로 구현한 플러그인 구조. 그리고 flint-mcp/는 라이브러리를 에이전트에게 MCP 도구(render_chart·compile_chart·validate_chart·list_chart_types·create_chart_view), 리소스(flint://chart-types·flint://agent-skill), 프롬프트(author_flint_chart)로 노출하는 법 — stdio/HTTP 두 트랜스포트, CSP-safe 인프로세스 래스터화까지 — 실전 교본이다.
agent-skills/flint-chart-author/SKILL.md(566줄)는 "LLM을 위한 지시문"의 마스터클래스 — 정확한 차트 타입 표, "변환/필드명을 지어내지 말라", 데이터 정합성 체크리스트. 순수 프롬프트 엔지니어링 자료로도 값지다. flint-py는 공유 픽스처(shared/test-data/) + 패리티 평가로 두 구현을 동기화하는 규율을 보여 준다.
npx -y flint-chart-mcp로 MCP 서버를 띄워 에이전트(Claude/VS Code)에 붙이고 CSV로 첫 차트를 그려 본다. ② semantic_types에서 Price↔Temperature↔Rank를 바꿔 가며 포맷·색상·축 방향이 어떻게 자동으로 달라지는지 관찰한다. ③ docs/adding-a-semantic-type.md를 보고 새 타입(Rating 등)을 하나 추가해 본다. 이 셋만 해도 "의미가 디자인을 결정한다"는 핵심이 몸에 붙는다.Flint은 특별한 하드웨어가 필요 없다 — 순수 계산이라 GPU도 무관하다. 라이브러리로 쓰든, 에이전트용 MCP 서버로 쓰든 설치는 간단하다.
# 1) 라이브러리로 쓰기
npm install flint-chart
npm install vega vega-lite vega-embed # 브라우저에서 Vega-Lite 렌더 시
npm install echarts # ECharts 백엔드
npm install chart.js # Chart.js 백엔드
# 2) 에이전트용 MCP 서버(전역 설치 불필요)
npx -y flint-chart-mcp
MCP 클라이언트(VS Code .vscode/mcp.json 또는 일반 mcpServers) 설정은 이렇게 한다.
{
"mcpServers": {
"flint-chart": { "command": "npx", "args": ["-y", "flint-chart-mcp"] }
}
}
| 항목 | 요구 / 설명 |
|---|---|
| 런타임 | Node ≥ 18(JS) · Python ≥ 3.10(소스 전용 프리뷰). 브라우저 표시엔 렌더러 라이브러리 필요 |
| 가속기 | 불필요 — 순수 스펙 변환. 무거운 건 렌더러가 담당 |
| MCP 트랜스포트 | --transport <stdio|http>(기본 stdio) · http는 --port/--host |
| 백엔드 제한 | --backends vegalite,echarts로 활성 백엔드 한정 가능 |
| 하드닝 모드 | --disable-file-reference — 에이전트가 로컬 파일을 못 읽고 data.values 인라인만 허용(원격 URL은 애초에 안 가져옴) |
| 데이터 입력 | 인라인 data.values 또는 로컬 .json/.csv/.tsv 경로(data.url) |
# 기여/개발 (저장소 루트에서)
git clone https://github.com/microsoft/flint-chart && cd flint-chart
npm install # workspaces: flint-js + flint-mcp + site
npm run typecheck # tsc
npm run test # Vitest (JS+MCP); npm run test:py → uv run pytest
npm run build # tsup가 두 npm 패키지 빌드
npm run site # http://localhost:5274 갤러리+에디터
assembleVegaLite·assembleECharts·assembleChartjs에 같은 입력을 넣고 출력 스펙 셋을 나란히 찍어 본다.
import { assembleVegaLite, assembleECharts, assembleChartjs } from 'flint-chart';
const input = {
data: { values: myData },
semantic_types: { weight: 'Quantity', mpg: 'Quantity', origin: 'Country' },
chart_spec: { chartType: 'Scatter Plot',
encodings: { x: {field:'weight'}, y: {field:'mpg'}, color: {field:'origin'} } },
};
assembleVegaLite(input); assembleECharts(input); assembleChartjs(input);
목표: "하나의 IL → 여러 타깃"이라는 컴파일러 핵심을 눈으로 확인.
같은 수치 열의 semantic_types만 Quantity → Price → Temperature → Rank → Percentage로 바꿔 가며 통화 포맷·0 기준선·발산 색상·역축·퍼센트 도메인이 자동으로 달라지는지 관찰한다. 202401 열은 YearMonth로 지정해 시간 축이 되는지 확인. 목표: Flint의 정체성 "의미 = 디자인"을 체감.
y 채널에 배열(y: ["sales","profit"])을 넣어 여러 측정 열을 계열로 접는 static-series fold를 써 보고, column: {field:"region"}으로 지역별 패널(facet)을 만든다. "fold가 color를 소유하므로 color를 따로 못 준다"는 제약도 확인. 목표: 내장 리셰이프와 facet 레이아웃 이해.
npx -y flint-chart-mcp를 Claude/VS Code에 연결하고, 로컬 CSV를 주며 "지역별 매출을 큰 순서로, region별 패널로 그려 줘"라고 시킨다. create_chart_view로 대화형 차트 뷰를 띄우고, validate_chart가 잡아 주는 실수(잘못된 차트 이름 등)를 관찰한다. 목표: "에이전트가 신뢰성 있게 차트를 그린다"는 워크플로 실측.
docs/adding-a-backend.md를 따라 Plotly나 D3용 코드 생성기를 새로 만든다. 1·2단계(시맨틱·레이아웃)는 손대지 않고 3단계 코드 생성만 구현하면 된다는 아키텍처의 힘을 직접 검증한다. 여유가 되면 docs/adding-a-chart-template.md로 새 차트 타입을 하나 추가. 목표: "백엔드 독립 프론트엔드"의 실전 이점을 체득.
| 주차 | 주제 · 학습 목표 |
|---|---|
| 1주차 시각화 문법 | Grammar of Graphics의 계보. Wilkinson의 그래픽 문법과 Vega-Lite(Satyanarayan 등)를 익혀 Flint이 "그 위에" 앉는 이유를 이해. Vega-Lite 스펙을 손으로 몇 개 써 보고, 왜 저장 타입만으론 부족한지 체감. |
| 2주차 컴파일러 이론 | 프론트엔드/IR/코드생성 분리. 중간표현(IR), 다중 타깃 코드 생성, 옵티마이저 패스 개념. Flint의 core/(타깃 독립)와 백엔드 코드생성기를 대조하며 "PL이 아닌 컴파일러"를 읽는다. |
| 3주차 MCP · 에이전트 도구 | 에이전트에 도구를 노출하기. Model Context Protocol(도구·리소스·프롬프트, stdio/HTTP), zod 스키마 검증, 서버측 래스터화. flint-mcp와 SKILL.md를 교본 삼아 "LLM을 위한 API·프롬프트 설계". |
| 4주차 타입 격자 · 군론 · 물리 | 불완전 입력을 견디는 설계. 타입 격자(lattice)와 graceful degradation, 군(orbit·generator·stabilizer)으로 상태 열거, 물리(스프링·가스 압력)를 레이아웃 휴리스틱으로. type-registry.ts·pivot.ts·compute-layout.ts를 정독. |
data + semantic_types(dataSpec) + chart_spec(chartSpec) + options.semantic_types=열의 의미(데이터셋 단위로 재사용), chart_spec=차트 타입+채널 바인딩(값싸게 편집하는 부분). 둘을 분리한 게 핵심 설계.{field, type?, aggregate?, sortOrder?, sortBy?, scheme?}를 채널에 바인딩. 맨 문자열은 {field} 축약.core/pivot.ts.render_chart·compile_chart·validate_chart·list_chart_types·create_chart_view 도구 + 566줄 flint-chart-author 스킬(flint://agent-skill 리소스로도 제공).저장소 & 배포
· GitHub: github.com/microsoft/flint-chart (MIT)
· npm 코어: npmjs.com/package/flint-chart · MCP: npmjs.com/package/flint-chart-mcp
프로젝트 사이트(문서 · 갤러리 · 라이브 에디터)
· 홈: microsoft.github.io/flint-chart
· 라이브 에디터: /#/editor · 갤러리: /#/gallery · MCP 가이드: /#/mcp
저장소 내 핵심 문서
· docs/architecture.md(3단계 + named-views 군론) · docs/overview.md(동기) · docs/design-semantics.md(시맨틱 3계층) · docs/design-stretch-model.md(물리 레이아웃 수식) · docs/api-reference.md · agent-skills/flint-chart-author/SKILL.md
제작
· Microsoft Research (microsoft.com/research) × 런민대 IDEAS Lab (ideas-lab.net) · 논문 "coming soon"