[Open WebUI] Sidebar 컴포넌트 메모리 누수 수정: onDestroy에서 onMount return으로 전환
PR 링크: open-webui/open-webui#22082 상태: Merged | 변경: +30 / -39
들어가며
Open WebUI의 Sidebar 컴포넌트에서 메모리 누수가 발생하고 있었습니다. onMount에서 등록한 이벤트 리스너와 스토어 구독을 onDestroy에서 해제하는 패턴인데, Svelte의 라이프사이클 타이밍 문제로 unsubscribers 배열이 모듈 스코프에 선언되어 컴포넌트 재생성 시 이전 구독이 누수될 수 있었습니다. 이 PR은 Svelte의 권장 패턴인 onMount 반환 함수로 클린업을 통합합니다.
핵심 코드 분석
Before: onMount + onDestroy 분리
let unsubscribers = []; // 모듈 스코프
onMount(async () => {
// 이벤트 리스너 등록
window.addEventListener('keydown', onKeyDown);
// ...
dropZone?.addEventListener('dragover', onDragOver);
$socket?.off('events', chatActiveEventHandler);
$socket?.on('events', chatActiveEventHandler);
unsubscribers = [
mobile.subscribe(...),
showSidebar.subscribe(...)
];
});
onDestroy(() => {
unsubscribers.forEach(u => u?.());
window.removeEventListener('keydown', onKeyDown);
// ...
$socket?.off('events', chatActiveEventHandler);
});
After: onMount 반환 함수로 통합
onMount(() => {
// 이벤트 리스너 등록
window.addEventListener('keydown', onKeyDown);
const unsubscribers = [ // 로컬 스코프
mobile.subscribe(...),
showSidebar.subscribe(...)
];
if (dropZone) {
dropZone.addEventListener('dragover', onDragOver);
}
const socketInstance = $socket;
socketInstance?.on('events', chatActiveEventHandler);
return () => { // 클린업 함수
unsubscribers.forEach(u => u());
window.removeEventListener('keydown', onKeyDown);
if (dropZone) {
dropZone.removeEventListener('dragover', onDragOver);
}
socketInstance?.off('events', chatActiveEventHandler);
};
});
왜 이게 좋은가
1. 소켓 인스턴스 캡처
기존 코드에서 $socket?.off()는 onDestroy 시점의 $socket 값을 사용하는데, 이 시점에 소켓이 이미 변경되었거나 null이 되면 원래 등록한 리스너를 해제하지 못합니다. 새 코드는 const socketInstance = $socket으로 등록 시점의 소켓을 캡처하여 정확한 해제를 보장합니다.
2. 클로저를 통한 안전한 참조
unsubscribers를 onMount 함수 안의 로컬 변수로 선언하고 반환 함수의 클로저로 캡처합니다. 모듈 스코프 변수를 사용할 때 발생할 수 있는 경쟁 조건이 제거됩니다.
3. dropZone null 체크 강화
기존의 dropZone?.addEventListener()는 옵셔널 체이닝으로 null을 무시했지만, 해제 시에도 동일한 조건 분기를 사용하지 않으면 불일치가 생깁니다. 새 코드는 if (dropZone) 블록으로 등록과 해제를 대칭적으로 처리합니다.
참고 자료
- Svelte 라이프사이클 문서 — onMount 반환 함수의 클린업 동작
- MDN: removeEventListener — 이벤트 리스너 해제 시 동일 참조 필요성
관련 포스트
PR Analysis 의 다른글
- 이전글 [Open WebUI] MentionList 컴포넌트 메모리 누수 수정
- 현재글 : [Open WebUI] Sidebar 컴포넌트 메모리 누수 수정: onDestroy에서 onMount return으로 전환
- 다음글 [Open WebUI] JSON.parse(JSON.stringify())를 structuredClone으로 교체
댓글