본문으로 건너뛰기

[Open WebUI] DOMParser 대신 html-entities로 HTML 디코딩 최적화

PR 링크: open-webui/open-webui#23165 상태: Merged | 변경: +3 / -3

들어가며

Open WebUI에서 LLM 응답을 스트리밍으로 표시할 때, 각 텍스트 토큰마다 unescapeHtml 함수가 호출됩니다. 기존 구현은 new DOMParser().parseFromString()을 사용하여 매번 완전한 DOM 문서를 생성했습니다. 이는 스트리밍 중 매 프레임마다 불필요한 DOM 트리 생성과 GC 부하를 발생시켰습니다.

핵심 코드 분석

Before: DOMParser로 전체 DOM 문서 생성

export function unescapeHtml(html: string) {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    return doc.documentElement.textContent;
}

DOMParser.parseFromString()은 전달받은 HTML 문자열로부터 완전한 Document 객체를 생성합니다. &, < 같은 간단한 HTML 엔티티를 디코딩하기 위해 HTML parser, DOM 트리 빌더, 메모리 할당이 모두 동작합니다.

After: html-entities 라이브러리의 decode 사용

import { decode } from 'html-entities';

export function unescapeHtml(html: string): string {
    return decode(html);
}

이미 프로젝트 의존성에 포함된 html-entities 라이브러리의 decode 함수를 사용합니다. 이 함수는 문자열 치환 방식으로 HTML 엔티티를 디코딩하므로 DOM 생성 없이 동작합니다.

왜 이게 좋은가

  1. GC 부하 감소: DOM 문서 객체 생성이 사라져 가비지 컬렉션 부하가 크게 줄어듭니다. LLM 스트리밍 중에는 이 함수가 초당 수십~수백 번 호출될 수 있어 영향이 큽니다.
  2. 의존성 추가 없음: html-entities는 이미 MarkdownTokens.svelte에서 같은 용도로 사용 중이므로 새로운 의존성을 추가하지 않습니다.
  3. 타입 안전성 향상: 반환 타입이 string | null에서 string으로 명확해졌습니다.

단 3줄의 변경이지만, hot path에서의 불필요한 객체 할당을 제거한 효과적인 최적화입니다. 브라우저 API가 항상 최적의 선택이 아닐 수 있다는 좋은 사례입니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글