더 나은 Agent UX 구축: 스트리밍 진행, 상태 및 파일 작업 with LangChain
발행: (2026년 1월 16일 오전 03:38 GMT+9)
3 min read
원문: Dev.to
Source: Dev.to
What you’ll build
간단한 패턴:
- 도구가 이벤트를 발생시킴 (진행 상황/상태/파일 작업) while it runs
- 프론트엔드가 구독하고 즉시 해당 이벤트를 렌더링
- 타입 가드가 UI 로직을 안전하고 예측 가능하게 유지
폴링 루프 없음. 추측 없음. “생각 중…” 같은 플레이스홀더도 없음.
1) Emit typed custom events from a tool call
도구 호출 내부에서 작업이 진행되는 동안 커스텀 이벤트를 작성합니다:
config.writer?.({
type: "progress",
id: analysisId, // stable id => update in place
step: steps[i].step,
message: steps[i].message,
progress: Math.round(((i + 1) / steps.length) * 100),
totalSteps: steps.length,
currentStep: i + 1,
toolCall: config.toolCall,
} satisfies ProgressData);
이것이 핵심 전환점입니다: 도구는 단순한 함수가 아니라 이벤트 생산자입니다.
2) Receive those events in React
UI에서 스트림 훅에 핸들러를 전달합니다:
onCustomEvent: handleCustomEvent,
이제 도구가 발생시킨 모든 이벤트가 실시간으로 클라이언트에 도착합니다.
3) Narrow event types and update UI state predictably
들어오는 이벤트를 unknown으로 취급한 뒤 타입 가드로 좁히고 id를 키로 하는 상태 맵을 업데이트합니다:
if (isProgressData(data)) {
/* update progress */
} else if (isStatusData(data)) {
/* update status */
} else if (isFileStatusData(data)) {
/* update file ops */
}
이렇게 하면 프론트엔드가 안정적으로 유지됩니다:
- 진행 상황이 제자리에서 업데이트
- 최소한의 재렌더링
- 문자열 기반 이벤트 스파게티 방지
Why it matters (beyond “nice UI”)
UI가 실제 실행을 반영할 때:
- 사용자는 에이전트를 더 신뢰하게 됨
- 디버깅이 크게 쉬워짐
- 로그를 파헤치지 않아도 실패 원인을 이해할 수 있음
- 더 나은 UX를 만들 수 있음: 단계 표시기, 타임라인, 파일 작업 피드 등
🎥 Video: