연구 블로그

수십 MB WASM을 한 번만 받게 만들기

생성형 AI2026.07.08 · 3분 읽기
수십 MB WASM을 한 번만 받게 만들기

본편("브라우저 녹음은 되는데 음성합성 API는 왜 거절할까")에서 오디오 변환을 브라우저 안의 WebAssembly 트랜스코딩 엔진으로 처리했다고 썼다. 거기서 한 문단으로 줄였던 이야기가 이 글이다. 무거운 WASM 의존성에서는 변환 속도보다 언제 받고, 몇 번 받느냐가 체감을 좌우했다.

필요할 때만 받는다

트랜스코딩 엔진 코어는 수십 MB짜리 WASM 바이너리다. 모든 방문자에게 페이지 진입 시 받게 하는 건 명백히 과했다. 음성 기능을 쓰지 않는 사용자가 대다수인데 그들에게 수십 MB를 미리 내려받게 할 이유가 없다. 그래서 지연 로딩을 썼다. 변환이 처음 필요한 순간에만 바이너리를 받는다.

지연 로딩은 경합을 부른다

지연 로딩은 새로운 문제를 만든다. 사용자가 음성 기능을 처음 누르는 순간, 그 한 번의 클릭이 여러 비동기 요청으로 갈라질 수 있다. 여러 컴포넌트가 거의 동시에 엔진을 요구하면 같은 수십 MB 바이너리를 중복으로 받기 시작한다.

그래서 로더를 세 겹으로 막았다. 한 번 초기화된 엔진은 메모리에 잡아두고 두 번째 호출부터 즉시 반환한다(싱글톤). 로딩이 진행 중일 때 들어오는 요청은 새 다운로드를 시작하지 않고 진행 중인 단일 Promise에 붙는다. 열 개의 동시 요청이 와도 다운로드는 한 번이다. 마지막으로 로딩 상태 플래그로 같은 초기화 루틴의 중복 진입을 막았다.

실패는 상태를 비워야 끝난다

로딩이 실패하면 거기서 끝내면 안 된다. 실패한 Promise를 공유 상태에 남겨두면 다음 시도가 그 실패에 영원히 매달린다. 그래서 실패 시 안내 메시지를 띄우고 공유 Promise를 비워 상태를 리셋했다. 다음 시도는 처음부터 다시 받는다. 한 번의 실패가 세션 전체의 음성 기능을 영구히 죽이지 않게 하는 안전장치다.

멀티스레드는 아무 데서나 켜지지 않는다

엔진의 멀티스레드 모드는 SharedArrayBuffer를 요구한다. 이 객체는 COOP·COEP 헤더가 적용된 교차출처 격리 환경에서만 쓸 수 있고, 헤더가 없는 환경에서는 아예 정의되지 않는다. 그래서 변환을 시도하기 전에 SharedArrayBuffer의 존재를 런타임에 감지해 분기한다.

정직하게 적어두면, 지금 코드는 감지에서 멈춘다. 미지원 브라우저를 위한 폴백 분기는 아직 없다. 감지가 곧 대응은 아니다.

정리

수십 MB 의존성의 로딩은 두 축으로 설계했다. 언제 받느냐는 지연 로딩이 답했고, 몇 번 받느냐는 싱글톤과 Promise 공유가 답했다. 그리고 실패 경로에서 상태를 비우는 한 줄이 이 모든 구조를 한 번의 네트워크 오류로 무너지지 않게 했다.

#WebAssembly#지연로딩#성능
수십 MB WASM을 한 번만 받게 만들기3분 읽기 · 0%