LLM이 차트를 그릴 때 생기는 문제
LLM에게 차트를 그려달라고 하면 코드는 곧잘 뽑는데, 다만 축 스케일, 여백, 라벨 겹침, 색 팔레트... 이런 저수준 디테일이 미묘하게 어긋나서 결과가 어딘가 어설프다.
값 하나 바뀌면 축 범위가 이상해지고, 카테고리가 많아지면 라벨이 서로 겹쳐서 손봐야 하는 경우가 종종 있다.
이건 LLM의 능력 문제라기보다, 애초에 차트 라이브러리의 API가 너무 많은 저수준 결정을 노출하기 때문이다. 마이크로소프트의 Flint 는 이 문제를 다른 방식으로 푼다. LLM이 저수준 디테일을 직접 다루게 하지 말고, 간결한 중간 언어(IR) 만 만들게 하자는 것이다.
핵심 아이디어: 중간 언어 + 컴파일러
Flint는 차트를 위한 중간 표현(intermediate representation) 이다. 흐름은 이렇다.
AI가 만드는 것 Flint 컴파일러가 처리하는 것
───────────── ──────────────────────────
차트 종류 축 스케일 계산
데이터 필드 매핑 → 여백·간격·라벨 배치
필드의 의미 범례·색 팔레트
(미묘하고 귀찮은 전부)AI는 무엇을 그릴지 만 간결하게 선언하고, 스케일·여백 같은 어떻게 그릴지 는 컴파일러가 결정론적으로 채운다. AI가 실수하기 쉬운 부분을 통째로 컴파일러에 떠넘기는 셈이다.
레이아웃도 자동이다. 데이터가 몇 개인지(카디널리티), 라벨이 얼마나 긴지에 따라 크기·간격·범례 위치를 알아서 조정한다. 그래서 카테고리가 5개일 때와 50개일 때 사람이 스펙을 고치지 않아도 각각 보기 좋게 나온다.
semantic types: 필드의 의미를 알려준다
Flint의 특징은 필드에 의미 타입(semantic type) 을 붙이는 것이다. Country, Price, Rank, Temperature 같은 70여 종의 의미 타입이 있고, 이걸 주면 축·색·스케일을 알아서 추론한다.
assembleVegaLite({
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" },
},
baseSize: { width: 400, height: 300 },
},
});origin 이 Country 라는 것만 알려주면, 국가에 어울리는 색 구분과 범례를 자동으로 잡아준다. 필드가 숫자인지 범주인지, 순서가 있는지 없는지에 따라 스케일이 달라지는 걸 사람이 지정하지 않아도 된다.
한 번 쓰면 여러 백엔드로
같은 Flint 스펙 하나가 Vega-Lite, ECharts, Chart.js 세 렌더러의 네이티브 스펙으로 컴파일된다. 40여 종의 차트 타입을 지원한다. 라이브러리로도, 에이전트용 MCP 서버로도 쓸 수 있다.
npm install flint-chart # 라이브러리
npx -y flint-chart-mcp # 에이전트용 MCP 서버MCP로 붙이면 대화 중에 에이전트가 차트를 만들어 인터랙티브 뷰를 열거나, 원하는 백엔드의 스펙을 바로 뽑아준다.
백엔드를 하나로 강제하지 않는 것도 실용적이다. 이미 ECharts를 쓰는 프로젝트면 ECharts로, 정적 이미지가 필요하면 Vega-Lite로 - 같은 의도를 렌더러만 바꿔 뽑을 수 있다. 렌더러 종속을 줄이고 IR을 한 층 위에 두는 방식이다.
왜 흥미로운가
Flint가 보여주는 건 요즘 자주 보이는 패턴이다 - AI는 중간 표현만 생성하고, 복잡한 결정은 결정론적 컴파일러가 맡는다. AI에게 최종 결과물을 통째로 시키는 것보다 안정적이고 제어하기 쉽다.
재밌는 점은, 이 간결한 스펙이 사람에게도 편하다 는 것이다. Flint는 AI 시대를 표방하지만, 결국 저수준 디테일은 컴파일러가 맡고 의도는 사람(또는 AI)이 맡는다는 좋은 추상화의 오래된 원칙을 다시 보여준다.
실습 1 - semantic type만 바꿔보기
위 예제에서 origin 의 의미 타입을 Country 대신 다른 걸로 바꾸면 결과가 어떻게 달라질지 예상해 보자.
Country → 국가에 맞는 범주형 색 팔레트 + 범례. 만약 순서가 있는 Rank 로 바꾼다면 연속적인 색 스케일 쪽으로 기울 것이다. 같은 데이터·같은 차트 종류라도 필드의 의미가 스케일과 색을 결정한다는 감각을 잡는 게 핵심이다.
실습 2 - IR + 컴파일러 패턴 눈에 익히기
Flint의 구조를 다른 곳에서도 찾아보자. 공통점은 AI/사용자는 간결한 IR을, 결정론적 엔진이 복잡한 디테일을 맡는다는 것이다.
- React JSX → 컴파일러가 실제 DOM 조작을 결정 (개발자는 무엇을 원하는지만 선언)
- SQL → 옵티마이저가 실행 계획을 결정
- Tailwind → 유틸리티 선언 → 빌드가 CSS 생성
Flint는 여기에 차트를 추가한 것이다. LLM 시대에 이 패턴이 다시 주목받는 이유를 생각해 보면 좋다.
레퍼런스
- Flint 공식 사이트 - 갤러리와 문서
- microsoft/flint-chart (GitHub) - 소스와 README
- GeekNews - Flint 소개 - 한국어 요약과 토론