Agentic UI 구현 패턴: Plan-and-Execute, Human-in-the-Loop, 실시간 스트리밍으로 에이전트 신뢰를 설계하는 방법
Gartner가 2026년 말까지 기업 애플리케이션의 40%가 태스크 특화 AI 에이전트를 통합한다고 예측했을 때, 저는 "설마"라고 생각했습니다. 2025년 초에 5%도 안 됐던 수치가 지금 이 속도로 치솟고 있는 걸 보면, 그게 먼 미래 얘기가 아니라는 걸 실감하게 됩니다. 더 충격적인 건 변화의 속도보다 어떻게 변하고 있느냐입니다.
솔직히 처음 "에이전트가 UI를 만든다"는 말을 들었을 때 반신반의했습니다. "그냥 챗봇 좀 더 똑똑하게 만든 거 아닌가?" 싶었거든요. 그런데 실제로 CopilotKit이나 LangGraph 기반 프로젝트를 들여다보니 패러다임 자체가 달라지고 있다는 걸 체감했습니다. 기존 AI가 "여기 결과물입니다"라고 텍스트를 던져주는 방식이었다면, Agentic UI는 에이전트가 직접 폼을 렌더링하고, 버튼을 생성하고, 상태를 업데이트하면서 작업을 완료해버립니다.
이 글은 React + TypeScript 환경을 중심으로 다루지만, 에이전트 백엔드 개발자에게도 직접적인 관련이 있습니다. Agentic UI는 프론트엔드만의 이야기가 아니라 에이전트와 UI 사이의 통신 계층 전체를 다시 설계하는 문제이기 때문입니다. 이 글에서는 Agentic UI의 핵심 설계 원칙 세 가지와 실무에서 직접 검증된 구현 패턴을 코드와 함께 살펴봅니다.
핵심 개념
"행동하는 AI"와 "제안하는 AI"의 차이
기존 생성형 AI와 Agentic UI의 차이를 한 문장으로 줄이면 이렇습니다.
Generative AI는 "어떻게 하면 되는지 알려드릴게요"라고 말하고, Agentic UI는 직접 해버립니다.
기술적으로 보면 단방향 요청-응답(Request/Response) 모델이 장기 실행 이벤트 스트림(Event Stream)으로 바뀌는 겁니다. 에이전트는 한 번의 응답으로 끝나지 않고 멀티스텝 태스크를 실행하면서 UI를 실시간으로 업데이트합니다.
| 구분 | 기존 Gen AI | Agentic UI |
|---|---|---|
| 상호작용 방식 | 요청 → 텍스트 응답 | 장기 실행 스트리밍 세션 |
| UI 역할 | 고정된 채팅 인터페이스 | 에이전트가 동적으로 생성·패치 |
| 사용자 역할 | 지시자 | 협력자 (승인·수정·위임) |
| 실행 주체 | 사람 | 에이전트 (with 사용자 감독) |
Agentic UI의 생애주기: 3단계로 쪼개보기
에이전트가 작업을 수행하는 과정을 세 단계로 나눌 수 있습니다. 실무에서 이 구분을 명확히 해두면 어디에 어떤 UI 패턴을 적용할지가 훨씬 뚜렷해집니다.
| 단계 | 목적 | 핵심 패턴 |
|---|---|---|
| Pre-Action | 사용자 동의 및 범위 확립 | 계획 가시화, 승인 요청 |
| In-Action | 실행 맥락 실시간 제공 | 추론 설명, 신뢰도 신호 표시 |
| Post-Action | 안전 보장 및 복구 | 실행 감사 로그, Undo, 에스컬레이션 |
Pre-Action 단계에서 에이전트가 "나 이런 것들 할 건데, 괜찮아요?"라고 먼저 물어보는 게 핵심입니다. 이 단계를 건너뛰면 사용자가 에이전트를 신뢰하기 어렵고, 실무에서 배포 후 가장 많이 민원이 들어오는 지점이 바로 여기입니다. Post-Action에서 Undo와 감사 로그를 기본 기능으로 설계에 포함해두는 것도 마찬가지입니다 — 에러는 필연적으로 발생하기 때문에, 복구 수단이 처음부터 있어야 합니다.
2026년 현재의 프로토콜 지형도
에이전트와 프론트엔드가 어떻게 통신할지 표준화하려는 경쟁이 치열합니다. 저도 처음에 AG-UI, A2UI, MCP, A2A를 한꺼번에 보고 혼란스러웠는데, 각 프로토콜의 역할을 먼저 짚고 나면 꽤 명쾌해집니다.
- MCP (Anthropic): 에이전트가 외부 API·데이터베이스를 호출하는 툴 통합 레이어
- A2A (Google): 여러 에이전트가 협력할 때 쓰는 에이전트 간 통신 프로토콜
- AG-UI (오픈소스, CopilotKit이 초기 구현 주도): 에이전트와 프론트엔드 사이의 실시간 이벤트 스트리밍 표준
- A2UI (Google, 2026): 에이전트가 대화 맥락에 맞는 UI를 기술하는 선언형 데이터 포맷
프로덕션 스택 3층 구조: MCP(툴 통합) + A2A(에이전트 간 협력) + AG-UI(에이전트↔프론트엔드)로 레이어를 나눠서 생각하면 이해하기 쉽습니다.
AG-UI는 17가지 이벤트 타입으로 텍스트 스트리밍, 툴 콜, 상태 동기화, 실행 제어를 처리합니다. LangChain, CrewAI, Mastra, PydanticAI 등 주요 에이전트 프레임워크가 공식 지원하고 있고, 여러 클라우드 벤더들도 AG-UI 엔드포인트 지원을 추가하는 방향으로 움직이고 있습니다.
A2UI는 Google이 공개한 오픈소스 프로젝트로, 실행 가능한 코드가 아닌 데이터 포맷으로 UI를 기술하기 때문에 UI 인젝션 공격을 구조적으로 차단한다는 보안 설계가 차별점입니다. UI 인젝션이 뭔지는 뒤에서 자세히 다룹니다.
Generative UI 스펙트럼
에이전트가 UI를 얼마나 자유롭게 생성할 수 있는지에 따라 세 가지 모드로 나뉩니다. 어느 하나가 정답이 아니고, 실제 프로덕션에서는 이 세 가지가 한 제품 안에 공존하는 경우가 많습니다.
| 모드 | 제어 수준 | 방식 | 대표 도구 |
|---|---|---|---|
| Controlled Generative UI | 높음 | 사전 빌드된 컴포넌트를 에이전트가 선택·렌더링 | AG-UI, useFrontendTool |
| Declarative Generative UI | 중간 | 에이전트가 선언형 데이터로 컴포넌트를 기술 | A2UI, Open-JSON-UI |
| Open-ended Generative UI | 낮음 | 에이전트가 UI를 완전히 자유롭게 생성 | MCP Apps, Custom UI |
자, 이제 이 개념들을 실제 코드로 어떻게 구현하는지 살펴볼게요. 위의 3단계 생애주기에 각각 대응하는 예시 세 개를 준비했습니다.
실전 적용
예시 1: Plan-and-Execute 패턴 (Pre-Action)
실무에서 가장 검증된 패턴입니다. 에이전트가 작업을 시작하기 전에 단계별 실행 계획을 사용자에게 먼저 보여주고, 사용자가 수정하거나 승인한 뒤에 실행이 시작됩니다.
CopilotKit의 useCopilotAction을 사용하면 에이전트 백엔드에서 plan을 전송했을 때 프론트엔드에서 바로 커스텀 컴포넌트를 렌더링하고, 사용자 응답을 다시 에이전트로 전달할 수 있습니다. renderAndWaitForResponse가 흥미로운 부분인데 — 기존의 useState + fetch 패턴과 달리, 이 옵션을 쓰면 UI가 렌더링된 순간부터 에이전트 실행이 자동으로 일시 중지됩니다. 사용자가 respond()를 호출하기 전까지 백엔드 에이전트가 그 자리에서 대기하는 거죠.
import { useCopilotAction } from "@copilotkit/react-core";
interface PlanStep {
id: string;
description: string;
estimatedDuration?: string;
}
type PlanResponse = {
approved: boolean;
steps?: PlanStep[];
rejectionReason?: string;
};
function AgentWorkspace() {
useCopilotAction({
name: "presentExecutionPlan",
description: "실행 계획을 사용자에게 제시하고 승인을 기다립니다",
parameters: [
{
name: "steps",
type: "object[]",
description: "실행할 단계 목록",
attributes: [
{ name: "id", type: "string" },
{ name: "description", type: "string" },
{ name: "estimatedDuration", type: "string", required: false },
],
},
{
name: "taskSummary",
type: "string",
description: "전체 작업 요약",
},
],
renderAndWaitForResponse: ({
steps,
taskSummary,
respond,
}: {
steps: PlanStep[];
taskSummary: string;
respond: (response: PlanResponse) => void;
}) => (
<PlanApprovalCard
steps={steps}
taskSummary={taskSummary}
onApprove={(modifiedSteps) =>
respond({ approved: true, steps: modifiedSteps })
}
onReject={(reason) =>
respond({ approved: false, rejectionReason: reason })
}
/>
),
});
return <div>{/* 에이전트 작업 공간 UI */}</div>;
}| 코드 포인트 | 역할 |
|---|---|
renderAndWaitForResponse |
렌더링과 동시에 에이전트 실행을 일시 중지 |
respond: (response: PlanResponse) => void |
타입이 명시된 콜백 — 사용자 응답을 에이전트 백엔드로 전달 |
modifiedSteps |
사용자가 승인 전에 계획을 직접 수정할 수 있는 여지 제공 |
예시 2: AG-UI 이벤트 스트림으로 에이전트 진행 상황 실시간 표시 (In-Action)
에이전트가 멀티스텝 작업을 실행하는 동안 사용자에게 "지금 뭐 하고 있는지"를 투명하게 보여주는 게 신뢰 구축의 핵심입니다. AG-UI 프로토콜을 직접 사용해 이벤트를 처리해볼 수 있습니다.
StateDelta 이벤트로 에이전트 내부 상태를 UI에 실시간 반영할 때는 JSON Patch 연산이 필요합니다. 여기서는 fast-json-patch 라이브러리의 applyPatch를 사용합니다.
// npm install fast-json-patch @ag-ui/client
import { applyPatch } from "fast-json-patch";
import { AbstractAgent, EventType } from "@ag-ui/client";
import { useState } from "react";
function useAgentStream(agentUrl: string) {
const [streamingText, setStreamingText] = useState("");
const [activeToolCall, setActiveToolCall] = useState<string | null>(null);
const [agentState, setAgentState] = useState<Record<string, unknown>>({});
const [error, setError] = useState<string | null>(null);
const runAgent = async (userMessage: string) => {
setError(null);
const agent = new AbstractAgent({ url: agentUrl });
// 텍스트 스트리밍: 에이전트 응답을 실시간으로 출력
agent.on(EventType.TextMessageContent, (event) => {
setStreamingText((prev) => prev + event.delta);
});
// 툴 콜: 에이전트가 외부 API를 호출할 때 인디케이터 표시
agent.on(EventType.ToolCallStart, (event) => {
setActiveToolCall(event.toolCallName);
});
agent.on(EventType.ToolCallEnd, () => {
setActiveToolCall(null);
});
// StateDelta: JSON Patch 형식으로 에이전트 상태를 점진적으로 동기화
// applyPatch는 document를 in-place 변경하므로 먼저 clone
agent.on(EventType.StateDelta, (event) => {
setAgentState((prev) => {
const cloned = structuredClone(prev);
applyPatch(cloned, event.delta);
return cloned;
});
});
agent.on(EventType.RunFinished, () => {
setStreamingText("");
});
try {
await agent.run({
messages: [{ role: "user", content: userMessage }],
});
} catch (err) {
const message =
err instanceof Error
? err.message
: "에이전트 실행 중 오류가 발생했습니다";
setError(message);
setActiveToolCall(null);
}
};
return { streamingText, activeToolCall, agentState, error, runAgent };
}이렇게 구성하면 에이전트가 데이터베이스를 조회하거나 외부 API를 호출할 때 activeToolCall 상태를 통해 "지금 주식 데이터를 가져오고 있습니다..." 같은 실시간 피드백을 줄 수 있습니다. 사용자 입장에서 "내 요청이 먹혔나?" 하는 불확실성이 사라지는 거죠.
한 가지 실무 팁을 드리자면, SSE(Server-Sent Events) 기반의 AG-UI 스트리밍 특성상 컴포넌트가 언마운트될 때 연결을 명시적으로 끊지 않으면 메모리 누수가 생길 수 있습니다. useEffect의 cleanup 함수에서 agent.abort() 또는 그에 상응하는 연결 종료 처리를 추가하는 것을 권장합니다.
예시 3: Human-in-the-Loop — 고위험 작업 전 승인 게이트 (Post-Action 진입 전)
결제, 이메일 발송, 외부 API 호출처럼 되돌리기 어려운 작업에는 반드시 사용자 승인 단계가 필요합니다. CopilotKit은 LangGraph의 interrupt 노드와 연동해서 이 패턴을 내장 기능으로 제공합니다.
type ApprovalResponse = {
approved: boolean;
reason?: string;
};
useCopilotAction({
name: "requestHighRiskApproval",
description: "고위험 작업 실행 전 사용자 명시적 동의를 요청합니다",
parameters: [
{ name: "actionType", type: "string" }, // "payment" | "email" | "api_call"
{ name: "actionDetails", type: "string" }, // 사람이 읽을 수 있는 설명
{ name: "riskLevel", type: "string" }, // "medium" | "high" | "critical"
{ name: "reversible", type: "boolean" }, // 실행 취소 가능 여부
],
renderAndWaitForResponse: ({
actionType,
actionDetails,
riskLevel,
reversible,
respond,
}: {
actionType: string;
actionDetails: string;
riskLevel: string;
reversible: boolean;
respond: (response: ApprovalResponse) => void;
}) => (
<ApprovalGate
actionType={actionType}
actionDetails={actionDetails}
riskLevel={riskLevel}
isReversible={reversible}
onApprove={() => respond({ approved: true })}
onDeny={(reason) => respond({ approved: false, reason })}
/>
),
});interface ApprovalGateProps {
riskLevel: string;
isReversible: boolean;
actionDetails: string;
onApprove: () => void;
onDeny: (reason: string) => void;
}
function ApprovalGate({
riskLevel,
isReversible,
actionDetails,
onApprove,
onDeny,
}: ApprovalGateProps) {
return (
<div className={`approval-gate risk-${riskLevel}`}>
<RiskBadge level={riskLevel} />
<p>{actionDetails}</p>
{!isReversible && (
<WarningBanner message="이 작업은 실행 후 되돌릴 수 없습니다" />
)}
<div className="actions">
<button onClick={onApprove}>승인하고 실행</button>
<button onClick={() => onDeny("사용자가 거부")}>거부</button>
</div>
</div>
);
}reversible 플래그를 UI에 명시적으로 표시하는 건 작은 디테일처럼 보이지만 사용자 신뢰에 실질적인 영향을 줍니다. "이건 취소 가능합니다"와 "이건 되돌릴 수 없습니다"를 시각적으로 구분해주면 사용자가 더 편안하게 에이전트에 권한을 위임하게 됩니다.
장단점 분석
장점
솔직히 Agentic UI의 가장 큰 이점은 생산성 향상이나 적응형 UX보다 "기다리는 경험 자체가 사라진다"는 것입니다. 스트리밍 이벤트 덕분에 에이전트가 5분짜리 멀티스텝 작업을 처리하는 동안 사용자는 진행 상황을 실시간으로 볼 수 있고, 그게 신뢰와 직결됩니다.
| 항목 | 내용 |
|---|---|
| 생산성 향상 | 반복적·멀티스텝 태스크를 에이전트가 대신 실행해 사용자 작업 부하가 대폭 줄어듭니다 |
| 적응형 UX | 맥락에 따라 UI가 동적으로 변하므로 각 사용자와 시나리오에 최적화된 경험을 제공합니다 |
| 프레임워크 독립성 | AG-UI 기반 아키텍처는 특정 벤더에 종속되지 않고 어떤 에이전트 백엔드와도 연결됩니다 |
| 실시간 가시성 | 스트리밍 이벤트로 에이전트 진행 상황을 즉시 표시해 대기 불확실성을 제거합니다 |
단점 및 주의사항
PwC와 CIO 리서치에 따르면 완전 자율 에이전트를 신뢰하는 사용자는 8%에 불과합니다. 이 수치가 저에게도 꽤 충격적이었는데 — 기술이 아무리 발전해도 신뢰가 없으면 사용자는 결국 에이전트를 쓰지 않습니다. 아래 주의사항들은 전부 이 신뢰 문제와 연결되어 있습니다.
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 신뢰 부족 | 완전 자율 에이전트를 편안하게 느끼는 사용자는 소수입니다 | 점진적 위임 모델 적용, 고위험 작업엔 반드시 HITL 게이트 배치 |
| 블랙박스 문제 | 추론 과정이 불투명하면 정확한 결과도 신뢰받지 못합니다 | In-Action 단계에서 에이전트의 추론을 UI에 노출 |
| 보안 취약점 | UI 인젝션, 프롬프트 인젝션, 데이터 유출 위험이 있습니다 | 선언형 데이터 포맷(A2UI) 활용, 입력 검증 강화 |
| 예측 불가능성 | 같은 입력에도 다른 실행 경로가 나올 수 있습니다 | UI를 비결정론적 동작에 맞게 설계, 에러 복구 경로를 기본으로 제공 |
| 거버넌스 복잡성 | 에이전트 수가 많아지면 기존 IT 감독 방식으로 관리가 불가능합니다 | 실행 감사 로그(Action Audit) 기본 내장, 에스컬레이션 경로 설계 |
UI 인젝션(UI Injection): 악의적 프롬프트가 에이전트를 통해 UI 자체를 조작하는 공격입니다. 예를 들어 에이전트가 외부 문서를 읽는 과정에서 해당 문서에 심어진 지시문이 에이전트 동작을 변조해 가짜 버튼이나 폼을 렌더링하게 만드는 방식입니다. A2UI가 실행 코드 대신 데이터 포맷을 사용하는 이유가 바로 여기에 있습니다.
실무에서 가장 흔한 실수
-
Post-Action 설계를 후순위로 미루는 것: "에이전트가 잘 하겠지"라는 가정으로 Undo 기능이나 감사 로그를 나중에 붙이려 했다가, 에이전트가 의도치 않은 동작을 했을 때 복구 수단이 없어지는 상황이 생깁니다. 에러는 필연적으로 발생하기 때문에 복구 경로를 처음부터 기본 기능으로 포함시키는 게 훨씬 낫습니다.
-
에이전트 추론 과정을 숨기는 것: "사용자가 내부 로직을 볼 필요 없다"는 판단으로 에이전트가 왜 그 행동을 했는지 UI에 노출하지 않으면, 기술적으로 완벽한 결과도 사용자가 신뢰하지 않습니다. "현재 고객 DB를 조회하고 있습니다" 한 줄이 신뢰도를 크게 바꿉니다.
-
처음부터 완전 자율 모드로 전환하려는 것: 처음엔 에이전트가 제안 수준에서 시작하고, 사용자가 신뢰를 쌓으면서 자율성 레벨을 단계적으로 높이는 방식이 훨씬 수월합니다. 첫 배포에서 바로 모든 권한을 줬다가 사용자 거부감이 생기면, 한번 생긴 불신은 되돌리기가 상당히 어렵습니다.
마치며
Agentic UI의 핵심은 에이전트가 얼마나 똑똑한가보다, 사용자가 에이전트를 얼마나 신뢰할 수 있는가에 달려 있습니다. 실제로 배포까지 해보면 기술적 완성도보다 투명성과 제어권 설계가 사용자 반응을 훨씬 크게 갈라놓는다는 걸 체감하게 됩니다. "에이전트가 지금 뭘 하는지", "이 작업을 취소할 수 있는지" — 이 두 가지 질문에 UI가 명확하게 답해줄 수 있느냐가 실제 채택률을 결정합니다. Plan-and-Execute 패턴, Human-in-the-Loop 게이트, 실시간 이벤트 스트리밍은 그 신뢰를 UI 레벨에서 설계하는 구체적인 방법입니다.
지금 바로 시작해볼 수 있는 3단계:
-
CopilotKit으로 첫 Agentic UI 경험해보기:
npx create-copilotkit-app@latest로 프로젝트를 생성하면useCopilotAction이 포함된 스타터가 바로 세팅됩니다. 여기서renderAndWaitForResponse로 간단한 승인 UI를 만들어보시면 Agentic UI가 어떻게 동작하는지 빠르게 체감할 수 있습니다. -
기존 LLM 기능에 Plan-and-Execute 패턴 얹어보기: 이미 LLM을 쓰고 있는 프로젝트가 있다면,
useCopilotAction으로 멀티스텝 작업 직전에 계획 확인 단계를 하나 추가해보는 것부터 시작할 수 있습니다. LangGraph를 쓰고 있다면interrupt노드를 사용해 동일한 패턴을 백엔드 레벨에서도 구현할 수 있습니다. 큰 아키텍처 변경 없이도 사용자 신뢰도가 달라지는 걸 확인할 수 있습니다. -
AG-UI 이벤트 타입 문서 살펴보기: AG-UI 공식 문서에서 17가지 이벤트 타입을 살펴보시면 "에이전트가 UI와 어떻게 통신하는가"에 대한 멘탈 모델이 잡힙니다. 특히
StateDelta와ToolCallStart/End가 실제 구현에서 가장 자주 쓰이는 이벤트입니다.
참고 자료
- Designing For Agentic AI: Practical UX Patterns | Smashing Magazine
- Beyond Generative: The Rise Of Agentic AI And User-Centric Design | Smashing Magazine
- AG-UI Overview | Agent User Interaction Protocol 공식 문서
- Introducing A2UI: An open project for agent-driven interfaces | Google Developers Blog
- The Developer's Guide to Generative UI in 2026 | CopilotKit Blog
- The State of Agentic UI: Comparing AG-UI, MCP-UI, and A2UI | CopilotKit
- Understanding AG-UI: The Standard for Agentic User Interfaces | ANGULARarchitects
- Generative UI: Understanding Agent-Powered Interfaces | CopilotKit
- Agentic AI Design Patterns: Enterprise Guide | AuFait UX
- Agentic AI UX Design: 5 UX Patterns That Work | Onething Design
- Production-Grade Agentic Apps with AG-UI: Real-Time Streaming Guide 2026 | Medium
- Building interactive agentic applications using ADK and AG-UI | Google Cloud Blog
- Secrets of Agentic UX: Emerging Design Patterns | UX Magazine
- Agentic AI has big trust issues | CIO
- The rise and risks of agentic AI | PwC
- CopilotKit GitHub Repository