결론부터
| 파일 | 크기 | 처리 |
|---|---|---|
| AI 실행 엔진 (wasm) | 25.02MiB | 두 조각으로 나눠 올림 |
| 드럼 분리 모델 | 171MB | 공개 저장소에서 받아 브라우저에 저장 |
| 나머지 앱 파일 | 작음 | 그대로 |
- 엔진 — 26,239,907바이트. 한도(26,214,400바이트)보다 25,507바이트 컸습니다. 반으로 나눠 약 13.1MB 두 개로 올리고, 앱이 받아서 이어붙여 씁니다.
- 모델 — 171,038,235바이트라 애초에 한도를 한참 넘습니다. 공개된 저장소에서 직접 받고, 지문(SHA-256)으로 검증한 뒤 브라우저 저장소에 넣어 두 번째부터는 받지 않습니다.
- 헤더 — 여러 CPU 코어를 쓰려면 앱 주소에 교차 출처 격리 헤더 두 줄이 필요합니다.
1. 25MiB를 살짝 넘은 실행 엔진 나누기
브라우저에서 AI 모델을 돌리는 엔진은 onnxruntime-web의 ort-wasm-simd-threaded.jsep.wasm 파일입니다. 그래픽카드(WebGPU)와 CPU를 둘 다 지원하는 판이라 큽니다.
Cloudflare Pages는 파일 하나당 25MiB까지 받습니다. 압축이나 다른 판으로 바꾸는 방법도 생각했지만, 엔진 바이트를 건드리면 결과가 달라질 위험이 있어 내용은 그대로 두고 자르기만 했습니다.
- 올리기 전 — 작은 스크립트로 wasm 파일을 절반씩 잘라
.wasm.0,.wasm.1두 파일로 저장합니다. 원본은 올리지 않습니다. - 앱에서 — 두 조각을 동시에 받아 순서대로 한 덩어리로 이어붙입니다.
- 엔진에 넘기기 — 이어붙인 바이트를
ort.env.wasm.wasmBinary에 넣어 주면, 엔진은 파일을 따로 받지 않고 이것을 씁니다.
이어붙인 결과는 원본과 바이트 단위로 같으므로 분리 결과도 같습니다. 엔진 버전을 올릴 때는 스크립트를 한 번 다시 돌리면 됩니다.
2. 171MB 모델은 올리지 않고 받아 쓰기
드럼 분리 모델(htdemucs)은 공개된 Git 저장소에 ONNX 형식으로 올라와 있고, 그 주소는 다른 사이트에서 받아 가는 것을 허용(CORS)합니다. 그래서 내 서버에 올리지 않고 방문자 브라우저가 직접 받게 했습니다.
- 처음 방문 — 모델을 받으면서 진행률을 보여 줍니다.
- 검증 — 받은 파일의 SHA-256 지문을 미리 적어 둔 값과 비교합니다. 하나라도 다르면 쓰지 않습니다. 중간에 끊기거나 바뀐 파일을 막기 위해서입니다.
- 저장 — 브라우저의 Cache Storage에 넣어 둡니다. 다음 방문부터는 크기만 확인하고 바로 꺼내 씁니다.
3. 여러 코어를 쓰기 위한 헤더 두 줄
CPU로 분리할 때 코어를 여러 개 쓰면 빠릅니다(이 앱은 최대 8개). 브라우저가 이걸 허락하려면 페이지가 "교차 출처 격리" 상태여야 하고, 그러려면 서버가 다음 두 헤더를 보내야 합니다.
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp
Cloudflare Pages에서는 사이트 폴더의 _headers 파일에 /drum/* 주소에만 이 두 줄을 적었습니다. 사이트 전체에 걸면 다른 출처에서 불러오는 이미지·글꼴 같은 것들이 막힐 수 있어서 앱 주소로 한정했습니다. 실제 사이트에서 두 헤더가 나가는 것을 확인했습니다.
이 헤더를 켜면 다른 출처의 파일은 그쪽이 허용해야만 불러올 수 있습니다. 그래서 엔진(1번)은 외부 CDN이 아니라 내 사이트에 직접 올렸고, 모델(2번)은 허용해 주는 저장소에서 받습니다.
정리
- 파일 크기 한도에 걸리면, 내용을 바꾸지 말고 잘라서 올리고 앱에서 이어붙이는 방법이 가장 안전했습니다.
- 한도를 크게 넘는 모델은 공개 저장소 + 지문 검증 + 브라우저 저장 조합으로 무료 호스팅에서도 쓸 수 있습니다.
- 멀티스레드는 헤더 두 줄이 조건이고, 그 헤더 때문에 외부 파일 불러오기 규칙이 엄격해진다는 점을 같이 고려해야 합니다.
앱은 드럼 추출기에서 볼 수 있습니다. 같은 앱의 그래픽카드 버그는 그래픽카드를 1초 만에 놓던 버그에 정리했습니다.
자주 묻는 질문
25MiB와 25MB는 다른가요?
다릅니다. 25MiB는 25×1024×1024 = 26,214,400바이트, 25MB는 보통 25,000,000바이트를 뜻합니다. Cloudflare Pages 한도는 25MiB입니다.
모델을 매번 내려받나요?
아닙니다. 처음 한 번 받아 브라우저에 저장하고, 다음 방문부터는 저장된 것을 씁니다. 브라우저 데이터를 지우면 다시 받습니다.
파일을 나누면 느려지지 않나요?
두 조각을 동시에 받기 때문에 한 파일을 받을 때와 큰 차이가 없습니다. 이어붙이는 작업은 메모리 안에서 순간적으로 끝납니다.
휴대폰에서도 되나요?
이 앱은 컴퓨터에서 쓰는 것을 기준으로 만들었습니다. 휴대폰은 171MB 모델을 받고 계산하는 데 메모리가 많이 들어 아직 제대로 시험하지 않았습니다.