
AI 에이전트 생태계에서 프로토콜 전쟁이 한창입니다.
Anthropic의 MCP(Model Context Protocol), Google의 A2A(Agent-to-Agent), 그리고 이번에 살펴볼 AG-UI(Agent User Interaction Protocol)까지... 우후죽순 나오는 프로토콜들을 보면서 "공부할게 또 생겼어?" 라는 생각이 매일 드는 것 같습니다.
매일매일 자신들의 방법론이 AGI 달성했다며 벤치마크 점수를 자랑하는 논문들이 쏟아집니다. 하지만 막상 뜯어보면 지나치게 특정한 태스크에서만 잘 작동하고, 실제 프로덕션에 적용하기는 어려운 경우가 대부분입니다. 그래서 요즘은 웬만큼 괜찮아 보이지 않으면 깊게 공부할 엄두가 잘 나지 않는 것 같습니다.
AG-UI는 조금 달랐습니다. 우선 Google, Microsoft, LangChain, CopilotKit 등 여러 기업들이 함께 만든 프로토콜이라는 점에서 눈길이 갔습니다.
실제로 공부해보니 아직 생태계가 막 활발하진 않지만, Agentic AI 상용화 시도가 계속 늘어날 것이 분명한 만큼 미리 공부해두면 충분히 값어치가 있겠다는 생각이 들었습니다.
여러분의 시간을 아끼기 위해, 다음 중 하나에 해당된다면 이 글을 읽어보시길 추천드립니다.
- 🤔 AG-UI, A2UI, CopilotKit이 무엇인지 궁금한 개발자
- 🤖 AI 에이전트를 웹 애플리케이션에 통합하는 개발자
- 📊 사용자에게 에이전트 진행 상황을 보여줘야 하는 서비스 개발자(프론트/백)
- 🤝 에이전트가 중간에 사용자 확인을 받아야 하는 Human-in-the-Loop 기능을 구현하는 개발자
핵심 내용:
send_event() 수동 호출 필요state.value = X 변경하면 자동으로 프론트엔드에 전송 AG-UI(Agent User Interaction Protocol)는 AI 에이전트와 사용자 인터페이스 간의 표준화된 통신 프로토콜입니다. 기존의 단순한 요청-응답 방식을 넘어서, 이벤트 기반의 양방향 통신을 통해 에이전트의 상태를 실시간으로 프론트엔드와 동기화할 수 있게 해줍니다.
AG-UI의 가장 흥미로운 점은 에이전트의 상태(state) 관리를 이벤트 형식으로 전달한다는 것입니다. 백엔드에서 에이전트가 작업을 수행하는 동안 발생하는 모든 상태 변화를 프론트엔드로 스트리밍하여, React 애플리케이션이 이를 실시간으로 반영할 수 있게 됩니다.

AG-UI는 다음과 같은 핵심 컴포넌트로 구성되어 있습니다:
각 컴포넌트가 유기적으로 연결되어 에이전트와 사용자 간의 매끄러운 상호작용을 가능하게 합니다.
AG-UI 프로토콜은 여러 프레임워크에서 구현되어 있습니다

이러한 다양한 구현체 덕분에 개발자는 자신의 기술 스택에 맞는 방식으로 AG-UI 프로토콜을 적용할 수 있습니다. Python 개발자라면 LangGraph를, Java 개발자라면 Spring AI를 선택하는 식으로 말이죠.
AG-UI 프로토콜에 대해 처음 들었을 때 이런 의문이 들 수 있습니다.
"그냥 프론트엔드에서 상태 관리하다가 한 번에 백엔드로 보내면 되는 거 아닌가?"
저도 처음엔 같은 생각을 했습니다. 하지만 이 질문에 답하기 위해서는 데이터 흐름의 방향을 구분해서 생각해야 합니다.
사용자가 프론트엔드에서 입력한 데이터를 백엔드로 전송하는 경우라면, AG-UI 없이도 충분할 것 같아 보입니다. 기존의 REST API나 GraphQL처럼 한 번에 요청을 보내는 방식이 더 간단하고 효율적일 수 있습니다.

하지만 백엔드에서 프론트엔드로 상태를 전달하는 경우는 이야기가 달라집니다. 특히 AI 에이전트처럼 다음과 같은 특성을 가진 경우에는 더욱 그렇습니다:
일반적인 REST API 방식으로 구현하면 다음과 같은 상황이 발생합니다:

이 방식의 문제점은,
실제로 요청 제출로부터 첫 토큰 수신(TTFT)까지 5초만 지나더라도 사용자로부터 "이거 고장 난거 아녜요?"라는 질문을 받게 되는 경우가 많습니다. 백엔드에서는 열심히 작업 중인데, 프론트엔드는 그저 로딩 스피너만 돌고 있으니 말이죠...
SSE(Server-Sent Events)나 WebSocket을 직접 사용하면 가능하긴 하지만, 매번 상태가 바뀔 때마다 수동으로 이벤트를 전송해야 합니다
# 😓 기존 방식: 상태 변경마다 수동으로 SSE 이벤트 전송
async def analyze_data(request):
# 상태 1: 생각 중
current_status = "thinking"
await send_sse_event(request, {"type": "thinking"}) # 수동 전송
# 분석 작업...
# 상태 2: 도구 호출
current_status = "calling_api"
await send_sse_event(request, {"type": "tool_call", "name": "api"}) # 수동 전송
# API 호출...
# 상태 3: 진행률
progress = 50
await send_sse_event(request, {"type": "progress", "value": 50}) # 수동 전송
# ... 매번 상태가 바뀔 때마다 이벤트 전송 코드 필요
문제점:
send_sse_event() 호출을 직접 작성해야 함AG-UI를 사용하면 상태 변수만 바꾸면 자동으로 프론트엔드에 전달됩니다.
# ✨ AG-UI 방식: 상태만 바꾸면 자동으로 동기화
async def analyze_data(state: AgentState):
# 상태 1: 생각 중
state.status = "thinking" # 자동으로 프론트엔드에 전송됨!
# 분석 작업...
# 상태 2: 도구 호출
state.status = "calling_api" # 자동 전송!
state.tool_name = "api"
# API 호출...
# 상태 3: 진행률
state.progress = 50 # 자동 전송!
# ... 변수만 바꾸면 끝! 이벤트 전송 코드 불필요
장점:
플로우를 시각화하면 다음과 같습니다

핵심 차이점
| 기존 SSE/WebSocket | AG-UI 프로토콜 | |
|---|---|---|
| 상태 전송 | await send_sse_event() 수동 호출 | state.value = X 자동 동기화 |
| 코드 복잡도 | 비즈니스 로직 + 이벤트 전송 로직 | 비즈니스 로직만 |
| 실수 가능성 | 이벤트 전송 누락 위험 | 상태만 바꾸면 자동 전송 |
| 유지보수 | 매번 이벤트 전송 코드 추가 필요 | 프레임워크가 알아서 처리 |
백엔드에서 변수 값이 바뀌면 별도의 코드 없이 프론트엔드도 자동으로 업데이트됩니다. 마치 React의 useState처럼 동작하지만, 이번에는 네트워크를 넘어서 작동하는 것이죠.
이러한 추상화 덕분에 개발자는 복잡한 스트리밍 로직을 신경 쓰지 않고, 비즈니스 로직에만 집중할 수 있게 됩니다. 이것이 바로 AG-UI 프로토콜이 제공하는 큰 가치라고 생각합니다.
AG-UI를 공부하다 보면 A2UI(Agent-to-UI)라는 용어도 자주 보게 됩니다. 이름도 비슷하고 둘 다 에이전트와 UI를 연결한다는 점에서 헷갈리기 쉽습니다. 하지만 둘은 완전히 다른 역할을 합니다.

(공식 홈페이지에서도 헷갈릴 수 있음을 알고 수차례 비교합니다)
AG-UI는 에이전트와 프론트엔드 간의 통신 방법을 정의하는 프로토콜입니다.

state.status = "thinking" → 자동으로 프론트엔드에 전송A2UI는 에이전트가 생성한 UI를 어떻게 표현할지를 정의하는 명세입니다. HTML처럼 UI 구조를 데이터로 표현하는 방식이죠.

AG-UI와 A2UI는 함께 사용될 수 있습니다

간단히 정리하면
| AG-UI | A2UI | |
|---|---|---|
| 목적 | 통신 프로토콜 | UI 표현 명세 |
| 담당 | 어떻게 전송할까? | 어떻게 보여줄까? |
| 비유 | HTTP (전송 방법) | HTML (표현 방법) |
| 핵심 기능 | 실시간 상태 동기화 | 동적 UI 생성 |
A2UI UI 명세는 LLM 에이전트가 생성합니다. 마치 에이전트가 HTML을 작성하는 것처럼, A2UI JSON을 생성해서 프론트엔드로 전달합니다.
# 에이전트가 A2UI 명세 생성
ui_spec = {
"type": "form",
"components": [
{"type": "input", "label": "이름", "name": "name"},
{"type": "button", "label": "제출", "action": "submit"}
]
}
# AG-UI를 통해 프론트엔드로 전송
state.ui = ui_spec # 자동으로 프론트엔드에 전송됨
프론트엔드는 이 A2UI 명세를 받아서 실제 UI 컴포넌트로 렌더링합니다. 프론트엔드 개발자가 매번 새로운 화면을 만들 필요 없이, 에이전트가 상황에 맞는 UI를 동적으로 생성할 수 있게 되는 것이죠.
a2ui composer 페이지에서 직접 a2ui를 이용한 페이지 생성 예시를 쉽게 확인할 수 있습니다.

AG-UI와 A2UI에 대해 알아봤으니, 이제 프론트엔드 개발자분들은 "그래서 이걸 어떻게 쓰지? AG-UI Client를 내가 직접 구현해야 하나?"라는 질문이 생길 것 같습니다. 바로 여기서 CopilotKit이 등장합니다.
CopilotKit은 웹 애플리케이션에 AI 에이전트를 통합하기 위한 React 기반 프레임워크입니다. AG-UI 프로토콜을 구현한 대표적인 프론트엔드 라이브러리라고 볼 수 있습니다.

CopilotKit을 사용하면 프론트엔드에서 능동적으로 AI 기능을 정의할 수 있습니다. 백엔드를 건드리지 않고도 프론트엔드에서도 도구(tool)를 정의하고 에이전트에게 제공할 수 있습니다.
// 프론트엔드에서 직접 도구 정의
<CopilotKit>
<CopilotAction
name="searchProducts"
description="상품을 검색합니다"
handler={async (query) => {
const results = await fetch(`/api/search?q=${query}`);
return results;
}}
/>
<YourApp />
</CopilotKit>
다만 이런 에이전트 내부 동작 기능을 프론트엔드에서 담당하는 것이 맞나... 라는 의문점은 가지고 있습니다.
LangGraph 같은 백엔드 에이전트와 통합하면, AG-UI 프로토콜을 통한 자동 상태 동기화가 가능합니다.
// 단 3줄로 AG-UI 연결
<CopilotKit
runtimeUrl="/api/copilotkit" // LangGraph 엔드포인트
agent="myAgent">
<CopilotChat />
</CopilotKit>
백엔드에서 state.status = "thinking" 같은 상태 변경이 자동으로 프론트엔드 UI에 반영됩니다. EventSource 연결, 메시지 파싱 같은 복잡한 작업은 CopilotKit이 알아서 처리합니다.

에이전트가 중요한 작업을 수행하기 전에 사용자 승인을 받아야 하는 경우가 많습니다. CopilotKit의 useHumanInTheLoop 훅을 사용하면 이를 쉽게 구현할 수 있습니다.
// 프론트엔드: 확인 UI 정의
useHumanInTheLoop({
name: "confirmDeletion",
description: "사용자 삭제 전 확인",
parameters: [
{ name: "userId", type: "string", description: "삭제할 사용자 ID", required: true }
],
render: ({ args, status, respond }) => {
if (status === "executing" && respond) {
return (
<div>
<p>정말로 사용자 {args.userId}를 삭제하시겠습니까?</p>
<button onClick={() => respond({ confirmed: true })}>삭제</button>
<button onClick={() => respond({ confirmed: false })}>취소</button>
</div>
);
}
return null;
},
});
# 백엔드 에이전트: 도구 호출만 하면 됨
@tool
def delete_user(user_id: str):
# confirmDeletion 도구 호출 (자동으로 프론트엔드 UI 표시 & 대기)
result = call_tool("confirmDeletion", userId=user_id)
if result["confirmed"]:
db.delete_user(user_id)
return "삭제 완료"
return "삭제 취소됨"
핵심 포인트
useHumanInTheLoop로 UI와 동작을 미리 정의respond()가 호출될 때까지 에이전트 실행이 자동으로 일시 중지
CopilotKit은 에이전트가 생성한 A2UI 명세를 자동으로 렌더링합니다. 또한 A2UI JSON이 올바른 형식인지 유효성 검사도 수행합니다.
// A2UI 자동 렌더링
{
"type": "card",
"title": "주문 확인",
"components": [
{"type": "text", "content": "총 금액: 45,000원"},
{"type": "button", "label": "결제하기", "action": "pay"}
]
}
위 A2UI 명세를 받으면 CopilotKit이 자동으로
| 기존 방식 | CopilotKit |
|---|---|
| EventSource 수동 연결 | 자동 AG-UI 연결 |
| 메시지 파싱 직접 구현 | 자동 메시지 처리 |
| UI 상태 수동 동기화 | 자동 상태 동기화 |
| HITL 로직 직접 구현 | useHumanInTheLoop 훅 제공 |
| A2UI 파싱 및 렌더링 직접 작성 | 자동 렌더링 + 유효성 검증 |
간단히 말하면, CopilotKit은 AG-UI와 A2UI 프로토콜을 React 애플리케이션에서 쉽게 사용할 수 있도록 도와주는 올인원 프레임워크입니다.
실제로 테스트를 해보기 위해 DeepAgents와 통합을 시도했습니다. LangGraph와의 통합은 문서화도 잘 되어 있고 비교적 순조로웠는데, DeepAgents에서 Sub-Agent를 호출하는 순간 예상치 못한 오류를 만났습니다.

처음에는 제가 뭔가 잘못 설정한 줄 알았습니다. 로그를 살펴보고 코드를 다시 확인해봤지만, 명확한 원인을 찾기 어려웠습니다. DeepAgents의 내부 구현을 들여다보니 AG-UI 프로토콜 처리 부분에서 SubAgent 호출 시 이벤트 스트림 처리가 제대로 이뤄지지 않는 것 같았습니다.
A2UI에 대한 기대가 컸습니다. "에이전트가 상황에 맞는 UI를 동적으로 생성한다"는 콘셉트가 너무 멋있게 들렸거든요. 더 이상 프론트엔드 개발자가 매번 새로운 화면을 만들 필요 없이, AI가 알아서 적절한 UI를 만들어준다니...
하지만 실제로 사용해보니 현실은 조금 달랐습니다.
가장 큰 아쉬움은 UI 생성 품질이었습니다. 현재의 LLM으로는 "기깔나는" UI를 만들기 어렵다는 걸 깨달았습니다. 결국 에이전트가 A2UI 명세를 생성하는 것도 일종의 코드 생성이고, 현재 프론티어 LLM들도 HTML/CSS 생성 테스트에서 아쉬운 모습을 보여주는 만큼 한계가 명확했습니다.
"이러면 그냥 HTML을 생성해서 보여주는 것과 크게 다를 게 있나?" 하는 의문이 들었습니다. A2UI가 HTML보다 생성형 AI에게 더 유리한 형식이라는 증거를 찾기 어려웠고, 표현력 자체도 제한적으로 느껴졌습니다.
물론 A2UI를 깊게 파보지는 않았기 때문에, 제가 놓친 부분이 있을 수도 있습니다. 하지만 적어도 현재 시점에서는 "에이전트가 아름다운 UI를 만들어준다"는 기대는 접는 게 나을 것 같습니다.
또 다른 아쉬움은 사용 가능한 컴포넌트가 제한적이라는 점이었습니다. 기본적인 폼, 버튼, 카드 정도는 지원하지만, 복잡한 인터랙션이나 커스텀 디자인이 필요한 경우에는 결국 프론트엔드 개발자가 직접 구현해야 했습니다.

결국 A2UI는 간단한 관리자 페이지나 내부 도구에는 적합할 수 있지만, 사용자를 대면하는 프로덕션 UI에는 아직 시기상조라는 생각이 들었습니다.
이런 한계들에도 불구하고, AG-UI 프로토콜 자체는 굉장히 의미 있는 시도라고 생각합니다.
저만 해도 지금 고민이 많습니다. 정부 프로젝트에서 Spring AI를 표준으로 삼겠다는 이야기가 나오면서, 그동안 익숙하게 사용하던 LangGraph/Langchin를 버리고 Spring AI를 공부해야 하나 되게 고민 중이거든요. 제가 LangGraph에 익숙해도 팀원들이 Java에 더 익숙하다면 결국 거기에 맞춰야 하기 때문에..
이런 상황에서 여러 회사들이 힘을 합쳐서 공통 프로토콜을 만든다는 것 자체가 굉장히 의미 있다고 봅니다. AG-UI 프로토콜을 따르기만 하면, 백엔드가 LangGraph든 Spring AI든, 프론트엔드는 동일한 방식으로 통합할 수 있으니까요.
아직은 초기 단계입니다. 생태계도 활발하지 않고, 베스트 프랙티스도 부족합니다. 하지만 이런 표준화 시도가 계속되다 보면 언젠가는:
이런 것들이 자연스럽게 형성될 것이라 기대합니다.
빠르게 어디선가 AG-UI를 활용한 멋진 사례가 나와서 "아, 이렇게 쓰는 거구나!" 하고 배울 수 있었으면 좋겠습니다.
AG-UI를 공부하면서 많은 것을 배웠습니다. 완벽한 기술은 아니지만, 방향성은 올바르다고 생각합니다. 에이전트와 UI를 연결하는 표준화된 방법이 있다는 것만으로도 충분히 가치가 있고, 현재도 활발하게 기여되고 있어 기대가 크게 됩니다.

여러분도 관심 있으시다면 한번 직접 사용해보시는 걸 추천드립니다. 함께 이 기술의 성장을 지켜보면서, 언젠가 더 나은 사례들을 나눌 수 있기를 기대해봅니다.