Workspace IndexKnowledge Notes › Real-time voice agents — replace the STT→LLM→TTS pipeline with one open bidirectional stream

#90PoC2026-09-19chat

Real-time voice agents — replace the STT→LLM→TTS pipeline with one open bidirectional stream

Google Cloud Tech published a walkthrough, "Build a real-time voice AI agent with Google ADK and Gemini Live API" (YouTube), built around a radio-DJ agent as the running example. Its starting claim: a voice agent built as three sequential stages — speech-to-text, then an LLM call, then text-to-speech — produces multi-second silences, because each stage has to finish completely before the next one can start (01:16). The fix it proposes is architectural, not a faster model: hold one persistent, bidirectional connection between the browser and Gemini's Live API, so audio flows continuously in both directions and either side can interrupt mid-sentence (01:34) — a phone call, not a walkie-talkie exchange. Google's Agent Development Kit (ADK) supplies the plumbing for this: an Agent configuration, a Runner that manages the call's lifecycle end to end, and a Session that has to stay in-memory for voice to stay fast (03:46, 08:09). The rest of the talk is concurrency: a `LiveRequestQueue` decouples sending browser audio from receiving model output, one call (`send_realtime`) for the continuous microphone stream and a separate call (`send_content`) for single finished payloads like typed text (05:27, 06:06), and an event stream that plays audio, shows captions, runs tool calls, and stops playback the instant the model reports the user interrupted (06:54, 07:04).

For Jayverse this is less a voice recipe than a template for any UI that has to react inside a live turn-taking loop instead of after a request completes — voice is just the sharpest case of it.

Why

A sequential pipeline puts a hard floor on latency: it cannot start producing a response until it has fully stopped listening, so the wait is the sum of three stages, not the slowest one. Faster STT or TTS models shrink that floor but never remove it, because the structure — listen fully, then think, then speak — is still three gated steps. Holding one open stream instead removes the gate, and it also unlocks something a pipeline cannot do at all by construction: accepting new input while a response is still being produced. There is no seam in a pipeline where "the user just started talking again" can land; in an open stream it is just another event. The same discipline — a queue that separates what you're sending from what you're receiving, so one slow leg of I/O never blocks the other — applies to any agent consuming a live event feed, not only audio.

How it works

The pipeline's silence, and the phone-call alternative

The old shape is STT → LLM → TTS, each stage blocking on the previous one's full output before starting (01:16). The video reframes the target as a persistent bidirectional connection rather than three request/response hops: audio keeps flowing both ways over a single channel, and the system has to support barge-in — the user cutting the agent off mid-sentence — as a first-class case, not an edge case (01:34).

The wiring: browser, WebSocket, ADK, Gemini Live

The browser owns the microphone, the speaker, and audio playback, and keeps one always-open WebSocket to the backend (02:02). The backend's ADK layer sits in the middle and relays the bidirectional stream between the client and the Gemini Live API without blocking on either side, using queues and a runner (02:11). Gemini Live is the part that does real-time audio understanding: it consumes the incoming audio directly and streams back both audio responses and structured events as they're ready, not after the whole utterance finishes (02:20).

ADK's three objects: Agent, Runner, Session

  • Agent is a configuration object: model, persona/instructions, and capabilities exposed as tools (02:48). Adding a new capability is just adding a Python function to the agent's tool list (03:04).
  • Runner executes the agent and owns the call's lifecycle from open to close (03:29). It normalizes every interaction — a spoken chunk, a tool call, an interruption — into an Event object the app consumes uniformly (03:37).
  • Session is where conversation state lives (03:46). For real-time voice this matters concretely: a remote session store adds network I/O on the state-read path, and for a channel where hundreds of milliseconds matter, that's the wrong trade — in-memory session storage is what keeps the round trip fast (03:46, 08:09).

The LiveRequestQueue: two ways in, one stream out

Sending and receiving are decoupled through a queue, which the video compares to a conveyor-belt sushi restaurant: producers and consumers work off the same belt without waiting on each other directly (04:46). One async task keeps pushing browser audio chunks onto the LiveRequestQueue as they arrive (upstream); a separate task keeps streaming model events back out to the browser (downstream) (05:03). Feeding the queue has two distinct calls for two distinct kinds of input: send_realtime is for an open-ended continuous stream like the microphone, and it has to keep sending audio through silence too, because Gemini's own voice-activity detection (VAD) needs that continuous signal to recognize where a sentence actually ends (05:27). send_content is for a single, already-complete piece of data where the user explicitly marked the end of input — typed text, an attached image (06:06).

Events: play, caption, call, or stop

The runner hands back one event stream, and the app dispatches on event type: audio chunks go straight to playback, transcript events go to the caption UI, and function-call events get executed immediately rather than queued (06:54). If the user starts talking over the agent, Gemini detects it and the Live API sends down an Interrupted event; the backend's job is to stop playback immediately on receiving it, not to let the current audio finish (07:04).

Where it lands in Jayverse

  • Rabbit: an Interrupted event has to reach the mandate layer, not just the speaker. If Rabbit ever ships a voice front end for an agent that moves money, "the user cut the agent off" is a stop signal with financial consequences — it should map to canceling the in-flight mandate, not just muting audio.
  • Verex: not an order-entry channel, but the event model still applies to fills. Voice input is the wrong UI for placing an order, but the pattern of "push structured events as they happen, dispatch by type, no waiting for a full response" is exactly what a live fill-notification feed needs.
  • Auditor: an Interrupted event is a fact worth logging. If a voice agent ever fronts something the Auditor watches, the interruption itself — when it happened, what the agent was saying, what it was told to stop — belongs in the audit trail, same as any other checked event.
  • Game: this is the wiring for a talking NPC. A street-level NPC that can hold a real back-and-forth (not scripted lines) needs exactly this shape: one open connection, VAD-aware continuous input, and barge-in so the player can interrupt.
  • Eng: a ready-made example for explaining latency budgets in an interview. "Why does a naive voice pipeline feel laggy, and how do you fix it structurally rather than by swapping models" is a concrete, well-scoped answer built from this video.

Verified and unverified

Verified on 2026-09-19: Google's Agent Development Kit (ADK) is a real open-source Python framework built around Agent, Runner, and Session concepts, with a LiveRequestQueue for bidirectional streaming to the Gemini Live API; the Gemini Live API is Google's real low-latency bidirectional audio API with built-in voice activity detection and interruption events; WebSocket is the standard browser transport for this kind of persistent connection. Taken from the video summary and not independently checked: the exact timestamps, the specific claim that STT→LLM→TTS pipelines produce "multi-second" silence, and the precise names/behavior of send_realtime and send_content as described (the general two-input-mode shape is consistent with how ADK's live API is documented, but exact call semantics were not independently verified here). Sources: YouTube — Build a real-time voice AI agent with Google ADK and Gemini Live API · related items: Tech #62 (agentic engineering writes the boundaries), the Homa item (homa-receiver-driven-transport) on tail latency and receiver-driven flow control, which pairs with this item's event-driven, receiver-reacts-immediately design.

Key expressions

Words and phrases from this page worth keeping, with the Korean meaning and the sentence they come from.

Expression뜻 · 쓰이는 자리
STTSpeech-to-Text(음성을 텍스트로 변환) · 옛 파이프라인의 첫 단계. "speech-to-text, then an LLM call, then text-to-speech"
LLMLarge Language Model(대형 언어 모델) · 파이프라인의 가운데 단계, 추론을 맡는 부분. "then an LLM call"
TTSText-to-Speech(텍스트를 음성으로 변환) · 옛 파이프라인의 마지막 단계. "text-to-speech"
ADKAgent Development Kit(구글의 에이전트 개발 키트) · Agent·Runner·Session 개념을 제공하는 오픈소스 파이썬 프레임워크. "Google's Agent Development Kit (ADK) supplies the plumbing"
VADVoice Activity Detection(음성 활동 감지) · 침묵 속에서 문장의 끝을 인식하는 기능, send_realtime이 계속 오디오를 보내야 하는 이유. "Gemini's own voice-activity detection (VAD)"
WebSocket웹소켓(브라우저와 서버 사이 상시 양방향 연결 프로토콜) · 음성 스트림을 나르는 표준 전송 수단. "keeps one always-open WebSocket to the backend"
barge-in말 끼어들기(상대가 말하는 도중에 끼어드는 것) · 전화 통화형 설계가 지원해야 하는 핵심 기능. "the user cutting the agent off mid-sentence"
turn-taking발화 순서 교대(대화에서 말할 차례를 주고받는 것) · 파이프라인이 아니라 열린 스트림이 필요한 이유. "a live turn-taking loop"
in-memory (session)인메모리(디스크·네트워크 없이 메모리에 상태를 두는 방식) · 원격 세션 저장소의 네트워크 I/O를 피해 지연을 줄이는 선택. "in-memory session storage is what keeps the round trip fast"
LiveRequestQueueADK의 실시간 요청 큐(송신·수신을 분리하는 큐 객체) · 업스트림과 다운스트림을 잇는 핵심 구조. "one async task keeps pushing browser audio chunks onto the LiveRequestQueue"
send_realtime연속 스트림 입력 함수(끝이 없는 데이터, 예: 마이크용) · 침묵 구간도 계속 보내야 VAD가 작동. "send_realtime is for an open-ended continuous stream"
send_content완성된 단일 입력 함수(사용자가 전송을 명시적으로 끝낸 데이터용) · 타이핑 텍스트·이미지 등에 사용. "send_content is for a single, already-complete piece of data"
upstream / downstream업스트림(보내는 방향) / 다운스트림(받는 방향) · 큐로 분리된 두 비동기 작업을 가리키는 말. "one async task... (upstream); a separate task... (downstream)"
non-blocking논블로킹(한쪽 작업이 다른 쪽을 막지 않는 방식) · ADK 층이 스트림을 중계하는 방식. "relays the bidirectional stream... without blocking on either side"
Interrupted event인터럽트 이벤트(사용자가 끼어들었을 때 모델이 내려보내는 신호) · 백엔드가 즉시 재생을 멈춰야 하는 신호. "the Live API sends down an Interrupted event"
persona페르소나(에이전트의 성격·지침 설정) · Agent 객체를 구성하는 세 요소 중 하나. "model, persona/instructions, and capabilities"
hard floor (on latency)지연의 바닥값(더 줄일 수 없는 최소 대기 시간) · 순차 구조 자체가 만드는 한계를 설명하는 표현. "A sequential pipeline puts a hard floor on latency"
gated step문지기 단계(앞 단계가 끝나야만 다음이 시작되는 구조) · 파이프라인의 근본적 결함을 가리키는 말. "three gated steps"
dispatch (on event type)타입별로 분기 처리하다 · 이벤트 스트림을 재생·자막·함수 호출로 나눠 처리하는 방식. "the app dispatches on event type"

← All Knowledge Notes · Workspace Index · Top ↑

실시간 음성 에이전트 — STT→LLM→TTS 파이프라인을 하나의 양방향 열린 스트림으로 바꾼다

Google Cloud Tech가 라디오 DJ 에이전트를 예시로 삼은 안내 영상 "Build a real-time voice AI agent with Google ADK and Gemini Live API"(YouTube)를 공개했다. 출발점은 이렇다. 음성 에이전트를 음성 인식(STT) → LLM 호출 → 음성 합성(TTS) 세 단계로 순차 구성하면, 각 단계가 앞 단계의 완료를 기다려야 하므로 수 초의 침묵이 생긴다(01:16). 영상이 제시하는 해법은 더 빠른 모델이 아니라 구조 변경이다. 브라우저와 Gemini의 Live API 사이에 하나의 영구 양방향 연결을 유지해 오디오가 양방향으로 끊김 없이 흐르고, 어느 쪽이든 말하는 도중에 끼어들 수 있게 한다(01:34) — 무전기가 아니라 전화 통화다. 구글의 Agent Development Kit(ADK)이 이를 위한 배관을 제공한다. Agent 설정, 통화 생명주기 전체를 관장하는 Runner, 그리고 음성에서 빠르게 응답하려면 인메모리로 유지해야 하는 Session이다(03:46, 08:09). 나머지는 동시성 이야기다. `LiveRequestQueue`가 브라우저 오디오 전송과 모델 출력 수신을 분리하고, 마이크처럼 끝없이 이어지는 스트림에는 `send_realtime`, 타이핑처럼 사용자가 전송을 명시적으로 끝낸 단일 완성 데이터에는 `send_content`를 쓰며(05:27, 06:06), 이벤트 스트림이 오디오를 재생하고 자막을 표시하고 도구 호출을 실행하고, 사용자가 끼어들었다는 신호가 오면 즉시 재생을 멈춘다(06:54, 07:04).

Jayverse에서 이것은 음성 레시피라기보다, 요청이 끝난 뒤가 아니라 실시간 턴 교환 루프 안에서 반응해야 하는 모든 UI를 위한 틀이다. 음성은 그중 가장 날카로운 사례일 뿐이다.

순차 파이프라인은 지연에 단단한 바닥을 깐다. 듣기를 완전히 멈추기 전까지 응답 생성을 시작할 수 없으므로, 대기 시간은 가장 느린 한 단계가 아니라 세 단계의 합이다. 더 빠른 STT나 TTS 모델은 그 바닥을 낮출 뿐 없애지 못한다. "완전히 듣고 → 생각하고 → 말한다"는 세 단계 게이트 구조 자체가 그대로이기 때문이다. 하나의 열린 스트림을 유지하면 이 게이트가 사라지고, 파이프라인 구조상 애초에 불가능한 것도 열린다. 응답을 만드는 도중에 새 입력을 받는 것이다. 파이프라인에는 "사용자가 다시 말하기 시작했다"가 들어갈 자연스러운 틈이 없지만, 열린 스트림에서는 그저 또 하나의 이벤트다. 보내는 것과 받는 것을 분리한 큐로 한쪽 I/O의 지연이 다른 쪽을 막지 않게 하는 이 원칙은, 음성뿐 아니라 실시간 이벤트 피드를 소비하는 모든 에이전트에 적용된다.

동작 방식

파이프라인의 침묵, 그리고 전화 통화라는 대안

기존 구조는 STT → LLM → TTS로, 각 단계가 앞 단계의 전체 출력을 기다려야 다음으로 넘어간다(01:16). 영상은 목표를 요청/응답 세 번의 왕복이 아니라 영구 양방향 연결로 재정의한다. 오디오가 하나의 채널로 양방향으로 계속 흐르고, 사용자가 에이전트의 말을 중간에 자르는 바깥 사례를 예외가 아니라 1급 기능으로 지원해야 한다(01:34).

배선: 브라우저, WebSocket, ADK, Gemini Live

브라우저가 마이크, 스피커, 오디오 재생을 직접 제어하며 백엔드와 상시 열린 WebSocket 하나를 유지한다(02:02). 백엔드의 ADK 층은 그 가운데서 클라이언트와 Gemini Live API 사이의 양방향 스트림을 큐와 러너를 이용해 어느 쪽도 막지 않고 중계한다(02:11). Gemini Live는 실시간 오디오 이해를 실제로 수행하는 부분으로, 들어오는 오디오를 바로 소비하고 발화 전체가 끝나기를 기다리지 않고 오디오 응답과 구조화된 이벤트를 준비되는 대로 스트리밍해 돌려준다(02:20).

ADK의 세 객체: Agent, Runner, Session

  • Agent는 설정 객체다. 모델, 페르소나·지침, 도구로 노출된 능력으로 구성된다(02:48). 새 능력을 추가하는 것은 파이썬 함수 하나를 에이전트의 도구 리스트에 넣는 일일 뿐이다(03:04).
  • Runner는 에이전트를 실행하고 통화 시작부터 종료까지 생명주기 전체를 관장한다(03:29). 발화 청크, 도구 호출, 인터럽트 등 모든 상호작용을 Event 객체로 정규화해 앱이 균일하게 소비하게 한다(03:37).
  • Session은 대화 상태가 저장되는 곳이다(03:46). 실시간 음성에서는 이것이 구체적인 의미를 갖는다. 원격 세션 저장소는 상태를 읽는 경로에 네트워크 I/O를 더하는데, 수백 밀리초가 중요한 채널에서는 잘못된 선택이다. 응답을 빠르게 유지하는 것은 인메모리 세션이다(03:46, 08:09).

LiveRequestQueue: 두 가지 입력 경로, 하나의 출력 스트림

송신과 수신은 큐로 분리되는데, 영상은 이를 회전초밥집에 비유한다. 생산자와 소비자가 같은 컨베이어 위에서 서로를 직접 기다리지 않고 일한다(04:46). 비동기 작업 하나는 도착하는 브라우저 오디오 청크를 계속 LiveRequestQueue에 밀어 넣고(업스트림), 다른 작업은 모델 이벤트를 브라우저로 계속 스트리밍한다(다운스트림)(05:03). 큐에 넣는 방법은 서로 다른 두 종류의 입력에 맞춰 두 가지다. send_realtime은 마이크처럼 끝이 정해지지 않은 연속 스트림용이며, 침묵 구간에도 계속 오디오를 보내야 한다. Gemini 자체의 음성 활동 감지(VAD)가 문장이 실제로 어디서 끝나는지 인식하려면 그 연속 신호가 필요하기 때문이다(05:27). send_content는 사용자가 전송의 끝을 명시적으로 표시한, 이미 완성된 단일 데이터용이다. 타이핑한 텍스트, 첨부한 이미지 같은 것이다(06:06).

이벤트: 재생, 자막, 호출, 또는 정지

러너는 이벤트 스트림 하나를 돌려주고, 앱은 이벤트 종류에 따라 분기한다. 오디오 청크는 바로 재생으로, 자막 이벤트는 캡션 UI로, 함수 호출 이벤트는 큐에 쌓지 않고 즉시 실행으로 보낸다(06:54). 사용자가 에이전트 위에 말을 얹기 시작하면 Gemini가 이를 감지하고 Live API가 Interrupted 이벤트를 내려보낸다. 백엔드가 할 일은 그것을 받는 즉시 재생을 멈추는 것이지, 현재 오디오가 끝나기를 기다리는 것이 아니다(07:04).

Jayverse에서의 위치

  • Rabbit: Interrupted 이벤트는 스피커가 아니라 만다트(mandate) 계층까지 닿아야 한다. Rabbit이 언젠가 돈을 움직이는 에이전트의 음성 프런트엔드를 갖춘다면, "사용자가 에이전트를 끊었다"는 재정적 결과가 따르는 정지 신호다. 단순히 음소거가 아니라 진행 중인 만다트 취소로 이어져야 한다.
  • Verex: 주문 입력 채널은 아니지만, 이벤트 모델은 체결 알림에 그대로 적용된다. 음성은 주문을 넣기에는 틀린 UI지만, "발생하는 대로 구조화된 이벤트를 밀어 보내고, 타입별로 분기하며, 전체 응답을 기다리지 않는다"는 패턴은 실시간 체결 알림 피드에 정확히 필요한 것이다.
  • Auditor: Interrupted 이벤트는 기록할 가치가 있는 사실이다. Auditor가 감시하는 무언가가 음성 에이전트를 프런트로 쓰게 된다면, 인터럽트 자체 — 언제 일어났는지, 에이전트가 무슨 말을 하고 있었는지, 무엇을 멈추라고 지시받았는지 — 도 다른 확인된 이벤트와 마찬가지로 감사 기록에 들어가야 한다.
  • Game: 말하는 NPC를 위한 배선이 바로 이것이다. 정해진 대사가 아니라 실제 주고받는 대화를 하는 거리 NPC에는 정확히 이 구조가 필요하다. 하나의 열린 연결, VAD를 인식하는 연속 입력, 그리고 플레이어가 끼어들 수 있는 인터럽트.
  • Eng: 면접에서 지연 예산을 설명할 때 바로 쓸 수 있는 예시다. "왜 단순한 음성 파이프라인은 느리게 느껴지고, 모델을 바꾸는 대신 구조적으로 어떻게 고치는가"는 이 영상에서 나온 구체적이고 범위가 명확한 답이다.

확인된 것과 미확인

2026-09-19 확인: 구글의 Agent Development Kit(ADK)은 Agent, Runner, Session 개념과 Gemini Live API로의 양방향 스트리밍을 위한 LiveRequestQueue를 갖춘 실제 오픈소스 파이썬 프레임워크다. Gemini Live API는 내장 음성 활동 감지와 인터럽트 이벤트를 갖춘 구글의 실제 저지연 양방향 오디오 API이며, WebSocket은 이런 영구 연결의 표준 브라우저 전송 방식이다. 영상 요약에서 가져왔고 독립 확인하지 않은 것: 정확한 타임스탬프, STT→LLM→TTS 파이프라인이 "수 초"의 침묵을 만든다는 구체적 주장, 그리고 설명된 send_realtime·send_content의 정확한 명칭과 동작(두 입력 모드로 나뉜다는 전반적 구조는 ADK의 라이브 API 문서화 방향과 부합하지만, 정확한 호출 시맨틱은 여기서 독립적으로 확인하지 않았다).

출처: YouTube — Build a real-time voice AI agent with Google ADK and Gemini Live API · 관련 항목: Tech #62(에이전틱 엔지니어링은 경계를 쓴다), Homa 항목(homa-receiver-driven-transport) — 꼬리 지연과 수신자 주도 흐름 제어를 다루며, 이 항목의 이벤트 주도·즉시 반응 설계와 짝을 이룬다.

핵심 표현

이 페이지의 영어 본문에서 배울 만한 단어와 표현, 뜻과 나온 자리.

Expression뜻 · 쓰이는 자리
STTSpeech-to-Text(음성을 텍스트로 변환) · 옛 파이프라인의 첫 단계. "speech-to-text, then an LLM call, then text-to-speech"
LLMLarge Language Model(대형 언어 모델) · 파이프라인의 가운데 단계, 추론을 맡는 부분. "then an LLM call"
TTSText-to-Speech(텍스트를 음성으로 변환) · 옛 파이프라인의 마지막 단계. "text-to-speech"
ADKAgent Development Kit(구글의 에이전트 개발 키트) · Agent·Runner·Session 개념을 제공하는 오픈소스 파이썬 프레임워크. "Google's Agent Development Kit (ADK) supplies the plumbing"
VADVoice Activity Detection(음성 활동 감지) · 침묵 속에서 문장의 끝을 인식하는 기능, send_realtime이 계속 오디오를 보내야 하는 이유. "Gemini's own voice-activity detection (VAD)"
WebSocket웹소켓(브라우저와 서버 사이 상시 양방향 연결 프로토콜) · 음성 스트림을 나르는 표준 전송 수단. "keeps one always-open WebSocket to the backend"
barge-in말 끼어들기(상대가 말하는 도중에 끼어드는 것) · 전화 통화형 설계가 지원해야 하는 핵심 기능. "the user cutting the agent off mid-sentence"
turn-taking발화 순서 교대(대화에서 말할 차례를 주고받는 것) · 파이프라인이 아니라 열린 스트림이 필요한 이유. "a live turn-taking loop"
in-memory (session)인메모리(디스크·네트워크 없이 메모리에 상태를 두는 방식) · 원격 세션 저장소의 네트워크 I/O를 피해 지연을 줄이는 선택. "in-memory session storage is what keeps the round trip fast"
LiveRequestQueueADK의 실시간 요청 큐(송신·수신을 분리하는 큐 객체) · 업스트림과 다운스트림을 잇는 핵심 구조. "one async task keeps pushing browser audio chunks onto the LiveRequestQueue"
send_realtime연속 스트림 입력 함수(끝이 없는 데이터, 예: 마이크용) · 침묵 구간도 계속 보내야 VAD가 작동. "send_realtime is for an open-ended continuous stream"
send_content완성된 단일 입력 함수(사용자가 전송을 명시적으로 끝낸 데이터용) · 타이핑 텍스트·이미지 등에 사용. "send_content is for a single, already-complete piece of data"
upstream / downstream업스트림(보내는 방향) / 다운스트림(받는 방향) · 큐로 분리된 두 비동기 작업을 가리키는 말. "one async task... (upstream); a separate task... (downstream)"
non-blocking논블로킹(한쪽 작업이 다른 쪽을 막지 않는 방식) · ADK 층이 스트림을 중계하는 방식. "relays the bidirectional stream... without blocking on either side"
Interrupted event인터럽트 이벤트(사용자가 끼어들었을 때 모델이 내려보내는 신호) · 백엔드가 즉시 재생을 멈춰야 하는 신호. "the Live API sends down an Interrupted event"
persona페르소나(에이전트의 성격·지침 설정) · Agent 객체를 구성하는 세 요소 중 하나. "model, persona/instructions, and capabilities"
hard floor (on latency)지연의 바닥값(더 줄일 수 없는 최소 대기 시간) · 순차 구조 자체가 만드는 한계를 설명하는 표현. "A sequential pipeline puts a hard floor on latency"
gated step문지기 단계(앞 단계가 끝나야만 다음이 시작되는 구조) · 파이프라인의 근본적 결함을 가리키는 말. "three gated steps"
dispatch (on event type)타입별로 분기 처리하다 · 이벤트 스트림을 재생·자막·함수 호출로 나눠 처리하는 방식. "the app dispatches on event type"

← 전체 기술 노트 · 워크스페이스 인덱스 · 맨 위 ↑