스트리밍
같은 답변을 같은 토큰으로 잘라 렌더러 다섯에 동시에 흘린다. 패널이 화면에 들어오면 시작한다.
눈여겨볼 것은 세 가지다 — 반쪽 마커(**굵, 여는 백틱)가 글자로 비쳤다 사라지는지, 조사 앞에서 끝나는
굵게(**설정(config)**을)가 끝까지 그려지는지, 강조나 표가 닫힐 때까지 멈춰 보이는지.
WASM 빌드를 불러오는 중…
WASM 빌드를 불러오는 중…
- react-markdown 은 토큰마다 누적본을 통째로 다시 그린다. 반쯤 쓴 마크업이 비쳤다 사라진다.
- Streamdown 도 다시 그리지만, 안 닫힌 구문을 먼저 보정한다.
- mdwire → Streamdown 은 mdwire 스트리머를 앞에 둔다. Streamdown 은 정규화한 누적본에
preview()를 붙인 것을 받는다 — 브릿지다. - mdwire
useMarkdownStream()은 붙들고 있는 것(열린 굵게, 표 행)도 입력이 여기서 끝난 것처럼 먼저 그린다. - mdwire
useMarkdownStream({ eager: false })는 확정된 것만 보여 준다. 한 번 보인 것은 바뀌지 않는 대신 강조·표에서 멈춰 보인다.
DESIGN.md 는 에이전트가 쓴 한국어 문서 347건으로 이걸 쟀다(Streamdown 2.6.0, react-markdown
10.1.0 + remark-gfm). mdwire 스트리머를 Streamdown 앞에 두면 이미 보인 글이 바뀌는 횟수가 60%쯤
줄고 마커가 글자로 비치는 일이 사라졌다. 확정분만 보여 주는 mdwire html 스트리머에는 둘 다 없었다.
다만 확정분만 보여 주면 중간중간 멈춘다 — 이 샘플을 토큰 간격 60ms 로 흘리면 가장 긴 멈춤이
1,389ms, 미리보기를 켜면 302ms 였다. 미리보기가 기본인 이유다. 자세한 것은
DESIGN.md 의 브릿지 절.
샘플 답변은 합성이지만, 그 안의 모양은 모두 실제 에이전트 출력에서 깨졌던 것이다.
이 데모의 코드
import { Streamer } from "@minjun0219/mdwire";
import { useMarkdownStream } from "@minjun0219/mdwire/react";
import { Streamdown } from "streamdown";
// 브릿지 — 정규화는 mdwire, 그리기는 Streamdown
const s = new Streamer("github-markdown");
let done = "";
for (const token of tokens) {
done += s.push(token); // 확정분. 다시 고쳐지지 않는다
setText(done + s.preview()); // + 붙든 것을 입력이 여기서 끝난 것처럼 그린 꼬리
}
done += s.finish();
setText(done);
// <Streamdown>{text}</Streamdown>
// 또는 React 에서 바로 그린다 — innerHTML 없이
const live = useMarkdownStream(); // 붙든 것도 먼저 그린다(기본)
const settled = useMarkdownStream({ eager: false }); // 확정된 것만
// tokens → live.push(token) … live.finish(); render {live.elements}전체 컴포넌트: site/src/components/StreamCompare.tsx. 같은 비교를 독립 Vite 앱으로, WASM 을 싣는 번들러 설정까지: examples/react-streaming.