DevOps 엔지니어들은 평소 서버 성능, 배포 현황, 에러율을 모니터링하며 여러 대시보드를 확인합니다. Grafana나 Datadog과 같은 모니터링, 시각화 도구들이 있지만, 일반적으로 사전에 구성한 대시보드를 사용하다 보니 새로운 관점의 분석이 필요할 때마다 쿼리 문법을 작성하거나 패널을 수동으로 구성해야 합니다. 예상치 못한 장애 패턴을 분석하려면 즉석에서 쿼리를 작성해야 해 초기 대응이 늦어질 수 있습니다.
만약 자연어 질문만으로 AI가 필요한 차트를 즉시 생성해 준다면 어떨까요? Generative UI는 사용자의 자연어 질문을 이해하고, 그에 맞는 UI 컴포넌트를 동적으로 생성하는 기술입니다. 이 글에서는 Generative UI를 활용해 자연어 질문으로 GitLab 데이터를 조회하고, 사용자 의도에 따라 적절한 차트를 동적으로 생성하는 방법을 알아보겠습니다.
Generative UI 개요
먼저 Generative UI의 개념과 필요성, 핵심 요소와 동작 방식을 살펴보겠습니다.
개념
Generative UI는 자연어 입력을 해석해 사전에 정의된 UI 컴포넌트 체계 안에서 해당 의도에 맞는 UI를 동적으로 생성하는 기술입니다. 이는 고정된 형태로 사전에 만든 대시보드를 보여주는 게 아니 라, 사용자의 요청 시점에 필요한 차트, 테이블, 인터랙티브 요소를 적절한 형태로 생성해 화면에 반영합니다. Generative UI는 임의의 UI를 무제한 생성하는 기술이 아니라, 시스템 지시와 후처리를 거쳐 제약되고 검증된, 안전한 UI를 생성하는 게 핵심입니다.
필요성
ChatGPT나 Gemini와 같은 LLM은 기본적으로 텍스트에 기반해 응답을 생성합니다. 그러나 복잡한 데이터를 분석할 때는 텍스트만으로 시계열 패턴, 지표 간 상관관계, 분포 변화 등을 직관적으로 파악하기 어렵습니다.
Generative UI는 자연어 입력을 기반으로 React 컴포넌트나 인터랙티브 UI 조각을 생성해 렌더링하도록 지원함으로써 이 한계를 보완합니다. 이로써 개발자가 모든 화면을 미리 구성할 필요 없이, 사용자의 요구에 따라 실시간으로 변화하는 UI를 조립, 생성할 수 있습니다.
핵심 요소
Google Research의 논문 “Generative UI: LLMs are Effective UI Generators”에서는 다음 세 가지 요소에 따라 Generative UI를 구현했다고 밝힙니다.

- 도구 접근(Tool access): 서버는 이미지 생성, 웹 검색과 같은 주요 도구로 접근을 제공합니다.
- 면밀히 설계된 시스템 지시(Carefully crafted system instructions): 시스템은 목표, 계획, 예시와 더불어 포맷팅, 도구 매뉴얼, 일반적인 오류를 피하는 팁을 포함한 기술 사양이 담긴 상세 지시에 따라 운영됩니다.
- 후처리(Post-processing): 모델의 출력은 잠재적인 문제를 해결하기 위해 일련의 후처리 과정을 거칩니다.
동작 방식
Generative UI 시스템의 동작 흐름은 다음과 같습니다.
- 사용자 요청: 사용자가 자연어로 질문하거나 작업을 지시합니다.
- 도구 결정, 실행: 시스템은 요청을 분석해 API 호출, 데이터 조회, 이미지 생성 등 필요한 도구를 판단하고 실행합니다.
- 시스템 지시 해석: LLM은 면밀히 설계된 시스템 지시를 참고해 어떤 UI 요소를 사용할지, 도구 결과를 어떻게 UI에 반영할지를 결정합니다.
- UI 코드 생성: LLM은 위 정보를 바탕으로 시스템이 정의한 UI 형식에 맞는 코드를 생성합니다. 구현 방식은 시스템마다 다를 수 있습니다.
- 후처리: 생성된 코드가 실행 가능한지, 보안상 안전한지, 시스템 제약을 준수하는지 검증 과정을 거칩니다.
- 렌더링: 검증된 코드가 샌드박스 환경에서 안전하게 실행돼 UI로 렌더링됩니다.
Generative UI는 위 과정을 거쳐 사용자의 자연어 의도를 이해하고, 그 의도에 적합한 UI를 안전하게 동적으로 생성해 즉시 화면에 반영할 수 있습니다.
Semi-Generative UI 기반 GitLab 데이터 시각화 실습
현재 완전한 Generative UI를 구현하는 건 LLM이 생성하는 UI의 예측 불가능성과 임의 코드 실행에 따른 보안 위험 등 여러 제약 사항이 있습니다. 그러나 시스템 프롬프트와 사전에 정의된 도구를 활용해 제한적이고 안전한 형태로 Generative UI를 구현하는 것은 충분히 가능합니다.
지금부터 GitLab 데이터베이스(DB) 스키마 정보를 시스템 프롬프트에 포함하고, 자연어 질의로 데이터를 자동으로 조회하며, 차트로 시각화하는 시스템을 구현하겠습니다. 예를 들어, “최근 30일간 일별 Merge request(MR) 생성 수를 막대 차트로 보여줘”와 같은 질문에 SQL을 자동으로 생성하고 차트를 렌더링할 수 있습니다.
1단계: 환경 변수 설정
먼저 .env 파일을 설정합니다. LLM 사용을 위한 API 키와 GitLab DB 연결을 위한 변수 설정이 필요합니다.
# .env
# Anthropic API
ANTHROPIC_API_KEY=ANTHROPIC_API_KEY_HERE
# GitLab Database
GITLAB_DB_HOST=GITLAB_DB_HOST_HERE
GITLAB_DB_PORT=GITLAB_DB_PORT_HERE
GITLAB_DB_NAME=GITLAB_DB_NAME_HERE
GITLAB_DB_USER=GITLAB_DB_USER_HERE
GITLAB_DB_PASSWORD=GITLAB_DB_PASSWORD_HERE
2단계: 시스템 프롬프트, 스키마 정의
- LLM이 사용자 요청과 맥락에 맞는 쿼리를 생성하도록 시스템 프롬프트를 작성합니다.
- 시스템 프롬프트는 이번 시나리오에서 사용할 projects, users, merge_requests 등에 대한 GitLab DB의 스키마 일부와 날짜 범위 지정을 위한 정보, 제약사항 등을 포함합니다.
- 스키마 구조는 GitLab 버전과 커스터마이징 여부 등에 따라 달라질 수 있습니다. DB와 서버에 미치는 영향을 최소화하기 위해 LIMIT를 사용하고, SELECT 문만 사용하도록 제약 사항을 추가합니다.
3단계: 백엔드 API 구현 (route.ts)
- LLM은 정의한 시스템 프롬프트를 바탕으로 쿼리를 생성합니다.
- 이후 env 파일에서 지정한 값으로 GitLab DB에 연결해 쿼리를 실행하고, 도구를 이용해 결괏값을 적절한 차트 형식으로 변환합니다.
// route.ts 일부
import { anthropic } from '@ai-sdk/anthropic'
import { streamText, tool } from 'ai'
import { z } from 'zod'
import { Pool } from 'pg'
// PostgreSQL 연결 풀
const pool = new Pool({
host: process.env.GITLAB_DB_HOST,
port: parseInt(process.env.GITLAB_DB_PORT || '5432'),
database: process.env.GITLAB_DB_NAME,
user: process.env.GITLAB_DB_USER,
password: process.env.GITLAB_DB_PASSWORD,
max: parseInt(process.env.GITLAB_DB_POOL_MAX || '10'),
})
// SQL 실행 함수
async function executeQuery(sql: string) {
const normalizedSql = sql.trim().toUpperCase()
if (!normalizedSql.startsWith('SELECT')) {
throw new Error('SELECT 쿼리만 허용됩니다')
}
const client = await pool.connect()
try {
const result = await client.query(sql)
return result.rows
} finally {
client.release()
}
}
// GitLab DB 스키마 정보
const GITLAB_SCHEMA = `
GitLab PostgreSQL 데이터베이스 스키마:
1. projects - 프로젝트 정보
- id, name, description, created_at, updated_at, creator_id, namespace_id, visibility_level
2. users - 사용자 정보
- id, username, name, email, created_at, state
3. merge_requests - Merge Request
- id, title, state_id (1=opened, 2=closed, 3=merged), author_id, target_project_id, created_at, merged_at
4. issues - 이슈
- id, title, state_id (1=opened, 2=closed), author_id, project_id, created_at, closed_at
5. namespaces - 네임스페이스 (그룹/사용자)
- id, name, path, type
6. project_statistics - 프로젝트 통계
- project_id, commit_count, repository_size, storage_size
7. events - 활동 이벤트
- id, project_id, author_id, action, created_at
`
// 시스템 프롬프트
function getSystemPrompt() {
const today = new Date()
const dateStr = today.toISOString().split('T')[0]
return `당신은 GitLab 데이터 분석 전문가입니다. 사용자의 요청에 따라 적절한 도구(tool)를 선택하여 데이터를 시각화합니다.
오늘 날짜: ${dateStr}
${GITLAB_SCHEMA}
사용 가능한 도구:
1. displayBarChart - 카테고리별 비교 (프로젝트별, 사용자별 등)
2. displayLineChart - 시간에 따른 추이 (월별, 일별 등)
3. displayPieChart - 비율/구성 (상태별 비율 등)
4. displayTable - 상세 데이터 목록
5. displayMetricCard - 핵심 지표 (총 개수, 평균 등 단일 숫자)
도구 선택 기준:
- 사용자가 "추이", "변화", "꺾은선", "라인" 요청시 → displayLineChart
- 사용자가 "비율", "구성", "파이", "원형" 요청시 → displayPieChart
- 사용자가 "목록", "리스트", "상세" 요청시 → displayTable
- 사용자가 "총", "전체", "몇 개" 등 단일 숫자 요청시 → displayMetricCard
- 기본값 → displayBarChart
SQL 작성 시 주의사항:
- SELECT 문만 사용
- 차트용: name, value 칼럼으로 반환 (AS 사용)
- 테이블용: 원하는 칼럼명 그대로 반환
- 메트릭용: value 칼럼 하나만 반환
- 최대 20개 행 제한 (LIMIT 20)
- 날짜 필터링: NOW() - INTERVAL 'N days' 사용
대화 맥락 이해:
- 이전 대화를 참고하여 의도 파악
- "90일"처럼 기간만 입력하면 이전 요청의 기간 변경
- "파이로"처럼 차트 타입만 입력하면 이전 데이터를 다른 차트로 변환`
}
export async function POST(request: Request) {
const { messages } = await request.json()
const result = streamText({
model: anthropic('claude-sonnet-4-5'),
system: getSystemPrompt(),
messages,
tools: {
displayBarChart: tool({
description: '막대 차트를 표시합니다. 카테고리별 비교에 적합합니다.',
parameters: z.object({
title: z.string().describe('차트 제목'),
sql: z.string().describe('실행할 SQL 쿼리 (name, value 칼럼 반환)'),
}),
execute: async ({ title, sql }) => {
const rows = await executeQuery(sql)
const data = rows.map((row: Record<string, unknown>) => ({
name: String(row.name || row[Object.keys(row)[0]]),
value: Number(row.value || row[Object.keys(row)[1]]) || 0,
}))
return { type: 'bar', title, data, sql }
},
}),
displayLineChart: tool({...}),
}),
displayPieChart: tool({...}),
}),
displayTable: tool({...}),
}),
displayMetricCard: tool({...}),
}),
},
maxSteps: 3,
})
return result.toDataStreamResponse()
}
4단계: 프론트엔드 렌더링 구현 (page.tsx)
page.tsx파일에서 구현한 코드를 바탕으로 LLM이 생성한 차트 데이터를 브라우저에 렌더링합니다.- 차트 타입에 분기 처리를 해 LLM의 응답에 따라 적절한 차트 종류를 선택, 생성할 수 있습니다.
- 대화 내역을 모델에 다시 제공하는 로직을 추가해 LLM이 사용자와의 대화 맥락을 이해하고 요청을 처리하는 기능을 구현할 수 있습니다.
// page.tsx 일부
const generateChart = async () => {
// 차트 타입 라벨
const chartTypeLabels: Record<ChartType, string> = { bar: '막대', line: '꺾은선', pie: '파이' }
// 차트 렌더링
const renderChart = () => {
const { chartType, data } = widget
if (chartType === 'pie') {
return (
<PieChart>
<Pie data={data} dataKey="value" nameKey="name">
{data.map((_, i) => <Cell key={i} fill={CHART_COLORS[i % CHART_COLORS.length]} />)}
</Pie>
</PieChart>
)
}
if (chartType === 'line') {
return (...)
}
}
구현 결과
이제 요청사항을 입력해 차트를 동적으로 생성하겠습니다. 시연을 진행한 시점은 2025년 12월 2일입니다.
Case 1: 막대 차트 생성
- 입력: "최근 30일간 일별 MR 생성 수를 막대 차트로 보여줘”
- 결과: 2025년 11월 2일부터 12월 2일까지 데이터를 막대 차트로 생성했습니다. 11월 2 ~ 3일과 11월 27일 이후 데이터는 없어 표시되지 않았습니다.
최근 30일간 일별 MR 생성 수 막대 차트

- LLM이 생성한 SQL 쿼리:
SELECT
TO_CHAR(created_at, 'MM-DD') AS name,
COUNT(*) AS value
FROM merge_requests
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY created_at::date, TO_CHAR(created_at, 'MM-DD')
ORDER BY created_at::date
LIMIT 20
Case 2: 차트 타입 변경
- 입력: "꺾은선 그래프로 보여줘”
- 결과: 동일한 데이터를 꺾은선 그래프로 변환했습니다. 대화 맥락을 이해해 이전 데이터를 새로운 차트 타입으로 표현합니다.
최근 30일간 일별 MR 생성 수 꺾은선 그래프

Case 3: 기간 변경
- 입력: "최근 15일 내역”
- 결과: 2025년 11월 18일부터 12월 2일까지 15일간의 데이터 4개를 꺾은선 그래프로 표현했습니다. LLM이 대화 맥락을 유지하며 날짜 범위를 조정했습니다.
최근 15일간 일별 MR 생성 수 꺾은선 그래프

한계점
Generative UI는 사용자 요청에 따라 데이터를 기반으로 필요한 차트, 테이블, 인터랙티브 요소를 적절한 형태로 즉시 생성한다는 점에서 큰 잠재력이 있습니다. 그러나 실제 환경에서 사용하려면 다음과 같은 제약 사항을 고려해야 합니다.
- 환각과 정확성 한계: LLM의 환각 현상 때문에 Generative UI는 존재하지 않는 테이블이나 칼럼을 참조할 수 있습니다. 스키마 정보를 프롬프트에 제공해도 복잡한 JOIN이나 로직을 가진 쿼리는 실패할 수 있습니다. 이를 완화하려면 스키마 기반 정적 검증, 샌드박스 쿼리 실행, 쿼리 템플릿 제한 등 추가 검증 계층을 도입해 LLM이 생성한 쿼리를 자동으로 점검하고 수정해야 합니다.
- 보안 위험과 제약 사항: 자연어를 SQL로 변환하는 과정에서 SQL 인젝션이나 민감 데이터 유출 위험이 있습니다. DB 접근 계정을 READ-ONLY로 설정하고, 쿼리 후처리 등으로 위험한 키워드를 차단하는 등 엄격한 검증을 거쳐야 합니다.
- DB 성능과 부하 문제: LLM이 임의로 생성한 쿼리는 최적의 성능을 보장하지 않습니다. 대량 데이터에 접근하면 DB 성능에 치명적인 영향을 줄 수 있으므로, LIMIT 설정, 쿼리 타임아웃, 인덱스 활용 검증 등이 필요합니다.
- 컨텍스트 용량과 토큰 부담: GitLab처럼 수백 개 이상의 테이블을 가진 대규모 DB의 전체 스키마를 프롬프트에 포함하면 토큰 비용이 급증하고 처리 속도에도 악영향을 끼칠 수 있습니다. 스키마 일부만 프롬프트로 제공하거나, RAG 등 다양한 기술로 컨텍스트 한계를 보완해야 합니다.
부록: Google의 Generative UI 체험
앞서 구현한 Semi-Generative UI는 사전 정의한 차트 도구 안에서 동작하는 제한적인 형태입니다. 반면 Google Research의 Dynamic View는 차트를 넘어 게임, 시뮬레이터, 미니 애플리케이션 등 더 다양한 형태의 인터랙티브 UI 뷰를 생성할 수 있습니다.
Google Dynamic View 개요
Dynamic View는 Google이 Gemini 모델과 제약된 UI 실행 환경을 결합해 만든 Generative UI 실험 기능입니다. 사용자의 자연어 입력에 따라 샌드박스 환경에서 실행되는 단일 인터랙티브 뷰 또는 UI fragment를 생성합니다.
체험 방법
현재 Dynamic View는 실험 기능으로, 미국 계정을 사용하면서 Google One AI Premium을 구독하는 사용자에게 우선 제공되고 있습니다. 직접 사용하기 어렵더라도 Google Research 블로그에서 다양한 결과물을 확인할 수 있습니다. 아래는 Dynamic View로 생성한 주사위 시뮬레이터 화면입니다.

맺음말
이번 글에서는 Generative UI를 활용해 자연어 질문으로 GitLab 데이터를 조회하고, 사용자 의도에 따라 적절한 차트를 동적으로 생성하는 방법을 살펴봤습니다. 앞으로 Generative UI 기술이 발전하면, 더 다양한 시각화와 복잡한 대시보드를 자동으로 생성할 수 있을 것입니다.
그러나 실무 환경에서는 시각화만으로는 부족합니다. 이제는 데이터 간의 관계를 이해하고 맥락 있는 통찰을 얻는 것이 더 중요합니다. 인포그랩은 이러한 과제를 해결하기 위해 데이터 온톨로지 프레임워크 ‘Mantis’를 개발하고 있습니다. Mantis는 데이터 간의 의미론적 관계를 파악해 DevOps팀, 개발팀, 경영팀이 필요한 통찰을 얻도록 지원하고, 전사적인 프로젝트 추적과 의사 결정에 기여하는 혁신적인 도구가 될 것입니다.
참고 자료
- Yaniv Leviathan·Dani Valevski·Yossi Matias, “Generative UI: A rich, custom, visual interactive user experience for any prompt”, Google Research, 2025-11-18, https://research.google/blog/generative-ui-a-rich-custom-visual-interactive-user-experience-for-any-prompt/
- Yaniv Leviathan·Dani Valevski·Matan Kalman·Danny Lumen·Eyal Segalis·Eyal Molad·Shlomi Pasternak·Vishnu Natchu·Valerie Nygaard·Srinivasan (Cheenu) Venkatachary·James Manyika·Yossi Matias, “Generative UI: LLMs are Effective UI Generators”, Google Research, 2025, https://generativeui.github.io/static/pdfs/paper.pdf
- Vercel AI SDK 공식 기술 문서, https://ai-sdk.dev/docs/introduction
지금 이 기술이 더 궁금하세요? 인포그랩의 DevOps 전문가가 알려드립니다.