[Open WebUI] JSON.parse(JSON.stringify()) 대신 structuredClone으로 딥 카피 최적화
PR 링크: open-webui/open-webui#21948 상태: Merged | 변경: +5 / -5
들어가며
Open WebUI의 ResponseMessage.svelte와 MultiResponseMessages.svelte는 스트리밍 중 히스토리 변경을 감지할 때마다 메시지 객체를 딥 카피합니다. 기존에는 JSON.parse(JSON.stringify(...))로 수행했는데, 이는 전체 객체를 JSON 문자열로 직렬화한 후 다시 파싱하는 이중 비용이 있습니다. 이 PR은 structuredClone()으로 교체합니다.
핵심 코드 분석
Before: JSON 라운드트립
let message = JSON.parse(JSON.stringify(history.messages[messageId]));
$: if (history.messages) {
if (JSON.stringify(message) !== JSON.stringify(history.messages[messageId])) {
message = JSON.parse(JSON.stringify(history.messages[messageId]));
}
}
After: structuredClone
let message = structuredClone(history.messages[messageId]);
$: if (history.messages) {
const source = history.messages[messageId];
if (source) {
if (message.content !== source.content || message.done !== source.done) {
message = structuredClone(source);
} else if (JSON.stringify(message) !== JSON.stringify(source)) {
message = structuredClone(source);
}
}
}
왜 이게 좋은가
1. 문자열 변환 제거
JSON.parse(JSON.stringify())는 객체를 JSON 문자열로 변환(O(n) 메모리 할당)한 후 다시 파싱(O(n) 파싱)합니다. structuredClone()은 Structured Clone Algorithm을 사용하여 중간 문자열 없이 직접 객체 그래프를 복제합니다.
2. 스트리밍 중 누적 비용
LLM 응답이 길어질수록 메시지 객체의 content 필드가 커집니다. 매 토큰마다 수 KB ~ 수십 KB의 문자열을 생성했다가 바로 GC 대상이 되는 패턴은 V8의 Young Generation GC 압력을 높입니다. structuredClone()은 이 문제를 완화합니다.
3. 타입 보존
JSON.parse(JSON.stringify())는 Date, Map, Set, undefined 등을 올바르게 처리하지 못합니다. structuredClone()은 이러한 타입을 정확히 복제합니다. 현재 메시지 객체에 이런 타입이 없더라도, 향후 확장 시 잠재적 버그를 예방합니다.
참고 자료
- MDN: structuredClone() — structuredClone API 명세
- HTML Spec: Structured Clone Algorithm — 알고리즘 동작 원리
관련 포스트
- [Open WebUI] CodespanToken에서 JS 트랜지션을 CSS 애니메이션으로 교체하여 메인 스레드 부하 제거
- [Open WebUI] TTS 문장 파싱을 showCallOverlay 가드로 감싸 불필요한 O(n^2) 연산 제거
- [Open WebUI] CodeEditor에서 EditorView 미해제로 인한 메모리 누수 수정
- [Open WebUI] UserMessage에서 JSON 직렬화 대신 structuredClone과 빠른 경로 비교 적용
- [Open WebUI] JSON.parse(JSON.stringify()) 를 structuredClone으로 교체
PR Analysis 의 다른글
- 이전글 [Open WebUI] MessageInput 컴포넌트 메모리 누수 수정: 비동기 이벤트 리스너 생명주기 관리
- 현재글 : [Open WebUI] JSON.parse(JSON.stringify()) 대신 structuredClone으로 딥 카피 최적화
- 다음글 [Open WebUI] 스트리밍 중 scrollToBottom을 rAF로 배치 처리하여 불필요한 리플로우 제거
댓글