본문으로 건너뛰기

[Open WebUI] CodespanToken에서 JS 트랜지션을 CSS 애니메이션으로 교체하여 메인 스레드 부하 제거

PR 링크: open-webui/open-webui#23258 상태: Merged | 변경: +21 / -19

들어가며

Open WebUI에서 스트리밍 중 렌더링되는 인라인 코드(codespan)마다 Svelte의 transition:fade가 적용되고 있었습니다. 이 트랜지션은 JS로 구동되어 메인 스레드에서 인라인 스타일을 적용하고 라이프사이클 콜백을 실행합니다. 스트리밍 중 수십~수백 개의 코드스팬이 렌더링되면 메인 스레드에 상당한 부하를 줍니다. 이 PR은 동일한 시각 효과(100ms fade)를 CSS @keyframes로 교체하고, 중복된 <code> 블록을 하나로 통합합니다.

핵심 코드 분석

Before: JS 트랜지션 + 중복 엘리먼트

<script>
  import { fade } from 'svelte/transition';
</script>

{#if done}
  <code class="codespan cursor-pointer" on:click={...}>
    {unescapeHtml(token.text)}
  </code>
{:else}
  <code transition:fade={{ duration: 100 }} class="codespan cursor-pointer" on:click={...}>
    {unescapeHtml(token.text)}
  </code>
{/if}

트랜지션을 조건부로 적용하기 위해 <code> 블록이 완전히 중복되어 있습니다.

After: CSS 애니메이션 + 단일 엘리먼트

<code
  class="codespan cursor-pointer {!done ? 'fade-in-token' : ''}"
  on:click={...}>
  {unescapeHtml(token.text)}
</code>

<style>
  @keyframes fade-in-token {
    from { opacity: 0; }
    to { opacity: 1; }
  }
  .fade-in-token {
    animation: fade-in-token 100ms ease-out;
  }
</style>

fade import가 제거되고, 조건부 클래스 하나로 전체 로직이 대체됩니다.

왜 이게 좋은가

  • 메인 스레드 해방: CSS 애니메이션은 브라우저의 컴포지터 스레드에서 처리됩니다. JS 트랜지션이 매 코드스팬마다 실행되던 것과 달리, 메인 스레드를 전혀 차지하지 않습니다.
  • 코드 중복 제거: {#if}/{:else}로 동일한 <code> 블록을 두 번 작성할 필요가 없어졌습니다.
  • 번들 크기 감소: svelte/transition에서 fade import가 제거됩니다.
  • 동일한 시각 효과: 100ms ease-out으로 사용자가 차이를 느끼지 못합니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글