에이전트 UI 프로토콜
AI 에이전트가 사용자 인터페이스를 선언적으로 구성하는 표준 프로토콜
AI 에이전트가 직접 사용자 인터페이스를 구성하는 시대가 열리고 있습니다. 인간이 클릭과 타이핑으로 조작하던 UI를, 이제 에이전트가 선언적 프로토콜로 생성하고 제어합니다.
에이전트 시대의 UI 패러다임 변화
전통적 UI에서는 개발자가 화면을 설계하고 사용자가 직접 조작합니다. 에이전트 기반 UI에서는 AI가 사용자의 의도를 해석하고, 적절한 UI를 선언적으로 구성하여 클라이언트에 전달합니다.
이 패러다임 전환의 핵심은 UI 생성의 주체가 바뀐다는 점입니다.
| 구분 | 전통적 UI | 에이전트 UI |
|---|---|---|
| UI 설계 주체 | 인간 개발자 | AI 에이전트 |
| 전달 형식 | 임페러티브 코드 | 선언적 JSON |
| 렌더링 시점 | 빌드 타임 | 런타임 (실시간) |
| 적응성 | 사전 정의된 반응형 | 컨텍스트 기반 동적 구성 |
| 개인화 | 설정 기반 | 의도 기반 |
3계층 에이전트 프로토콜 아키텍처
에이전트 생태계는 세 가지 프로토콜 계층으로 구성됩니다. 각 계층이 담당하는 역할이 명확히 분리되어 있으며, 이 조합이 완전한 에이전트 UI 파이프라인을 형성합니다.
MCP (Model Context Protocol)
에이전트에게 도구와 컨텍스트를 제공하는 기반 계층입니다. 디자인 시스템의 컴포넌트 카탈로그, 토큰 정의, 패턴 문서를 MCP 서버를 통해 에이전트에 노출합니다.
A2A (Agent-to-Agent Protocol)
Google 주도로 표준화된 에이전트 간 통신 프로토콜입니다. 복잡한 UI 작업을 여러 전문 에이전트가 분담할 때 사용합니다. 예를 들어 레이아웃 에이전트와 데이터 시각화 에이전트가 협업하여 하나의 대시보드를 구성할 수 있습니다.
UI 프로토콜 (A2UI + AG-UI)
에이전트가 최종 사용자와 상호작용하는 계층입니다. A2UI는 UI의 "무엇(what)"을, AG-UI는 통신의 "어떻게(how)"를 담당합니다.
A2UI (Agent-to-User Interface)
Google이 주도하고 Apache 2.0 라이선스로 공개한 오픈 프로젝트입니다. AI 에이전트가 선언적 컴포넌트 설명(JSON)을 전송하면, 클라이언트가 이를 해석하여 네이티브 위젯으로 렌더링합니다.
A2UI 핵심 원칙
에이전트는 실행 코드를 보내지 않습니다. 사전 승인된 컴포넌트 카탈로그에서 선택한 선언적 데이터만 전송하므로, XSS 등의 보안 위험이 원천 차단됩니다.
설계 철학
- 선언적 데이터: 실행 코드가 아닌 JSON 구조체로 UI를 기술
- 사전 승인 카탈로그: 클라이언트가 허용한 컴포넌트만 사용 가능
- LLM 친화적: 플랫 리스트 + ID 참조 구조로 점진적 렌더링에 적합
- 플랫폼 중립: 웹, 모바일, 데스크톱 등 어디서든 네이티브 위젯으로 변환
A2UI 메시지 구조
{
"a2ui_version": "1.0",
"components": [
{
"id": "card_1",
"type": "Card",
"properties": { "title": "주문 요약", "elevation": 2 },
"children": ["text_1", "actions_1"]
},
{
"id": "text_1",
"type": "Text",
"properties": { "content": "총 3건의 주문이 있습니다.", "variant": "body" }
},
{
"id": "actions_1",
"type": "ActionGroup",
"properties": {
"layout": "horizontal",
"actions": [
{ "label": "결제하기", "action_id": "checkout", "variant": "primary" },
{ "label": "취소", "action_id": "cancel", "variant": "outline" }
]
}
}
]
}플랫 리스트 구조
A2UI는 컴포넌트를 중첩하지 않고 플랫 리스트로 나열합니다.
부모-자식 관계는 children 배열의 ID 참조로 표현합니다.
이 구조 덕분에 LLM이 토큰을 순차적으로 생성하면서 점진적으로 UI를 구성할 수 있습니다.
2026년 현황
A2UI는 2026년 v1.0 안정화 단계에 진입했습니다. Google의 Gemini, 주요 클라우드 플랫폼에서 지원이 확대되고 있으며, 컴포넌트 카탈로그 표준화가 진행 중입니다.
AG-UI (Agent-User Interaction Protocol)
CopilotKit 팀이 주도하는 오픈소스 프로토콜입니다. HTTP 기반 JSON 이벤트 스트리밍으로 에이전트와 프론트엔드 간 실시간 양방향 통신을 표준화합니다.
이벤트 스트리밍 아키텍처
AG-UI는 17가지 이벤트 타입을 정의합니다.
| 카테고리 | 이벤트 | 설명 |
|---|---|---|
| 라이프사이클 | RUN_STARTED | 에이전트 실행 시작 |
RUN_FINISHED | 에이전트 실행 완료 | |
RUN_ERROR | 에이전트 실행 에러 | |
| 메시지 | TEXT_MESSAGE_START | 텍스트 메시지 시작 |
TEXT_MESSAGE_CONTENT | 텍스트 스트리밍 청크 | |
TEXT_MESSAGE_END | 텍스트 메시지 완료 | |
| 도구 호출 | TOOL_CALL_START | 도구 호출 시작 |
TOOL_CALL_ARGS | 도구 인자 스트리밍 | |
TOOL_CALL_END | 도구 호출 완료 | |
| 상태 관리 | STATE_SNAPSHOT | 전체 상태 스냅샷 |
STATE_DELTA | JSON Patch 상태 변경 | |
| 사용자 입력 | CUSTOM | 커스텀 이벤트 (HITL 등) |
AG-UI 이벤트 스트림 예시
// AG-UI 이벤트 스트림 (Server-Sent Events)
{ "type": "RUN_STARTED", "threadId": "th_abc", "runId": "run_123" }
// 텍스트 메시지 스트리밍
{ "type": "TEXT_MESSAGE_START", "messageId": "msg_1", "role": "assistant" }
{ "type": "TEXT_MESSAGE_CONTENT", "messageId": "msg_1", "delta": "주문을 " }
{ "type": "TEXT_MESSAGE_CONTENT", "messageId": "msg_1", "delta": "확인하겠습니다." }
{ "type": "TEXT_MESSAGE_END", "messageId": "msg_1" }
// 도구 호출
{ "type": "TOOL_CALL_START", "toolCallId": "tc_1", "toolName": "getOrders" }
{ "type": "TOOL_CALL_ARGS", "toolCallId": "tc_1", "delta": "{\"userId\":\"u_456\"}" }
{ "type": "TOOL_CALL_END", "toolCallId": "tc_1" }
// 상태 업데이트 (JSON Patch)
{ "type": "STATE_DELTA", "delta": [
{ "op": "replace", "path": "/orders", "value": [{ "id": "ord_1", "status": "배송중" }] }
]
}
{ "type": "RUN_FINISHED", "threadId": "th_abc", "runId": "run_123" }A2UI vs AG-UI 비교
두 프로토콜은 경쟁이 아닌 상호 보완 관계입니다.
| 구분 | A2UI | AG-UI |
|---|---|---|
| 핵심 질문 | UI를 "무엇으로" 구성할 것인가 | 에이전트와 "어떻게" 통신할 것인가 |
| 주도 | CopilotKit | |
| 전달 방식 | 선언적 JSON (컴포넌트 설명) | 이벤트 스트림 (SSE) |
| 목적 | 플랫폼 중립 UI 렌더링 | 실시간 양방향 인터랙션 |
| 스트리밍 | 컴포넌트 점진적 생성 | 텍스트/도구/상태 이벤트 |
| HITL | 액션 콜백 | 커스텀 이벤트 |
| 조합 | AG-UI 위에서 A2UI 컴포넌트 전달 가능 | A2UI 컴포넌트를 이벤트에 포함 가능 |
실전 조합 패턴
AG-UI의 CUSTOM 이벤트에 A2UI JSON을 실어 보내면,
실시간 스트리밍 통신(AG-UI) 위에서 선언적 UI(A2UI)를 구성하는
풀스택 에이전트 UI 파이프라인이 완성됩니다.
에이전트 UX 디자인 패턴
에이전트 UI에서 반복적으로 등장하는 핵심 패턴을 정리합니다.
Intent Preview (Plan Summary)
에이전트가 행동하기 전에 계획을 사용자에게 보여주는 패턴입니다. 사용자는 계획을 승인, 수정, 거부할 수 있습니다.
interface IntentPreview {
planId: string
title: string
steps: {
description: string
tool: string
estimatedImpact: 'low' | 'medium' | 'high'
}[]
requiresApproval: boolean
estimatedDuration: string
}Streaming Response
실시간으로 텍스트와 UI를 점진적으로 생성하여 보여주는 패턴입니다. 사용자는 결과가 완성되기 전에 진행 상황을 확인할 수 있습니다.
Confidence Indicator
에이전트 응답의 신뢰도를 시각적으로 표시하는 패턴입니다. 신뢰도가 낮은 부분은 별도 강조하여 사용자의 검증을 유도합니다.
Human-in-the-Loop (HITL)
위험도가 높은 작업에서 에이전트가 사용자에게 명시적 확인을 요청하는 패턴입니다.
Ambient Intelligence
백그라운드에서 조용히 동작하다가 필요한 순간에만 UI에 나타나는 패턴입니다. 예를 들어 코드 편집 중 접근성 위반을 감지하면 인라인 경고를 표시하거나, 폼 작성 중 실시간으로 유효성을 검증하여 제안을 띄웁니다.
디자인 시스템에서의 에이전트 UI 컴포넌트
에이전트 UI를 디자인 시스템에 통합하려면 전용 컴포넌트와 토큰이 필요합니다.
에이전트 UI 컴포넌트 설계
// 에이전트 응답 메시지
interface ChatMessageProps {
role: 'user' | 'assistant' | 'system'
content: string
isStreaming?: boolean
confidence?: number // 0-1 신뢰도
sources?: SourceRef[] // 참조 출처
}
// 도구 호출 카드
interface ToolCallCardProps {
toolName: string
status: 'pending' | 'running' | 'completed' | 'error'
args?: Record<string, unknown>
result?: unknown
}
// 계획 미리보기
interface PlanPreviewProps {
steps: PlanStep[]
currentStep?: number
onApprove: () => void
onReject: () => void
onModify: (feedback: string) => void
}
// 사용자 확인 대화상자 (HITL)
interface ConfirmDialogProps {
title: string
description: string
impact: 'low' | 'medium' | 'high' | 'critical'
details?: string[]
onConfirm: () => void
onCancel: () => void
}에이전트 UI 전용 디자인 토큰
// tokens/agent-ui.ts
export const agentTokens = {
// 메시지 배경색
'agent.message.bg.user': 'var(--color-primary-50)',
'agent.message.bg.assistant': 'var(--color-neutral-50)',
'agent.message.bg.system': 'var(--color-warning-50)',
// 상태별 액션 색상
'agent.action.pending': 'var(--color-neutral-400)',
'agent.action.running': 'var(--color-blue-500)',
'agent.action.completed': 'var(--color-green-500)',
'agent.action.error': 'var(--color-destructive)',
// 신뢰도 (0-1 범위)
'agent.confidence.high': 'var(--color-green-500)', // 0.8+
'agent.confidence.medium': 'var(--color-yellow-500)', // 0.5-0.8
'agent.confidence.low': 'var(--color-orange-500)', // 0.2-0.5
'agent.confidence.veryLow': 'var(--color-red-500)', // 0-0.2
// 영향도
'agent.impact.low': 'var(--color-green-100)',
'agent.impact.medium': 'var(--color-yellow-100)',
'agent.impact.high': 'var(--color-orange-100)',
'agent.impact.critical': 'var(--color-red-100)',
// 스트리밍 · 간격
'agent.streaming.cursor': 'var(--color-primary)',
'agent.bubble.padding': 'var(--spacing-4)',
'agent.bubble.radius': 'var(--radius-lg)',
} as const컴포넌트-프로토콜 매핑
A2UI 컴포넌트를 디자인 시스템 컴포넌트로 매핑하는 전략입니다.
// a2ui-component-map.ts
const componentMap: Record<string, React.ComponentType<unknown>> = {
'Card': DSCard, 'Text': DSText, 'DataTable': DSDataTable,
'ActionGroup': DSActionGroup, 'Form': DSForm,
'Chart': DSChart, 'Alert': DSAlert, 'Image': DSImage,
}
function renderA2UI(components: A2UIComponent[]): React.ReactNode {
const roots = components.filter(
(c) => !components.some((p) => p.children?.includes(c.id))
)
return roots.map((comp) => {
const Component = componentMap[comp.type]
if (!Component) return null // 알 수 없는 타입 → 폴백
const children = comp.children?.map((id) =>
renderA2UI(components.filter((c) => c.id === id))
)
return <Component key={comp.id} {...comp.properties}>{children}</Component>
})
}