본문으로 건너뛰기

[Open WebUI] Notes.svelte 메모리 누수 수정

PR #21963 - fix: fix memory leaking of Notes.svelte

들어가며

Open WebUI의 Notes.svelte 컴포넌트에서도 ChatControls와 동일한 패턴의 메모리 누수가 발견되었습니다. onMount가 비동기 함수(async)로 선언되어 있고, onDestroy에서 동기적으로 이벤트 리스너를 해제하고 있었습니다. 비동기 onMount가 완료되기 전에 onDestroy가 호출되면, 이벤트 리스너가 해제되지 않는 문제가 발생했습니다.

핵심 코드 분석

Before

onMount(async () => {
    // 동기적 초기화
    dropzoneElement?.addEventListener('dragover', onDragOver);
    dropzoneElement?.addEventListener('drop', onDrop);
    dropzoneElement?.addEventListener('dragleave', onDragLeave);
});

onDestroy(() => {
    clearTimeout(searchDebounceTimer);
    const dropzoneElement = document.getElementById('notes-container');
    if (dropzoneElement) {
        dropzoneElement?.removeEventListener('dragover', onDragOver);
        dropzoneElement?.removeEventListener('drop', onDrop);
        dropzoneElement?.removeEventListener('dragleave', onDragLeave);
    }
});

After

onMount(() => {
    // 동기적 초기화
    dropzoneElement?.addEventListener('dragover', onDragOver);
    dropzoneElement?.addEventListener('drop', onDrop);
    dropzoneElement?.addEventListener('dragleave', onDragLeave);

    return () => {
        clearTimeout(searchDebounceTimer);
        if (dropzoneElement) {
            dropzoneElement?.removeEventListener('dragover', onDragOver);
            dropzoneElement?.removeEventListener('drop', onDrop);
            dropzoneElement?.removeEventListener('dragleave', onDragLeave);
        }
    };
});

왜 이게 좋은가

  1. async 제거: onMount에서 불필요한 async 선언을 제거하여, Svelte가 반환된 정리 함수를 올바르게 인식합니다. (async 함수는 Promise를 반환하므로 Svelte가 정리 함수로 처리하지 않습니다.)
  2. 같은 DOM 참조 보장: onDestroy에서 document.getElementById로 새로 요소를 찾는 대신, onMount 클로저 내의 동일한 dropzoneElement 참조를 사용합니다.
  3. 불필요한 console.log 제거: onDestroy에 있던 디버깅용 console.log('destroy')도 함께 정리되었습니다.
  4. 경쟁 조건 해소: onMount 반환값으로 정리 로직을 제공하면, Svelte 프레임워크가 마운트/언마운트 순서를 올바르게 관리합니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글