문제 발생: 정적 배포 환경에서 WASM 자산 404 에러
domi-indexed-sqlite는 @sqlite.org/sqlite-wasm을 기반으로 하여 브라우저 환경에서 SQLite를 사용할 수 있게 돕는 라이브러리입니다. 개발 환경(예: npm run dev)에서는 문제없이 잘 작동하던 이 라이브러리가 GitHub Pages와 같은 정적 호스팅 환경에 배포되자마자 오류를 뿜어내기 시작했습니다. 브라우저 개발자 도구를 열어보니, sqlite3.wasm과 sqlite3-worker1.js 같은 핵심 WASM 관련 파일들이 404 Not Found 에러를 반환하며 로드되지 못하는 것을 확인했습니다.
이 라이브러리는 IndexedDB를 기본 백엔드로 사용하며, OPFS나 Memory 모드도 지원합니다. 특히, CDN 폴백 탑재 기능을 통해 복잡한 WASM 자산 번들러 설정 없이 작동하도록 설계되어 있습니다. 하지만 정적 배포 환경에서는 이 설계가 예상대로 작동하지 않았습니다.
초기 진단과 시행착오: 번들링 설정과 상대 경로
가장 먼저 의심한 부분은 빌드 과정이었습니다. tsup.config.ts 파일을 확인하여 sqlite-wasm 관련 파일들이 빌드 결과물에 제대로 포함되거나 참조되는지 확인했습니다. tsup은 TypeScript 프로젝트를 위한 간단한 번들러로, 일반적으로 .js 파일을 중심으로 번들링하며 .wasm 같은 비-JS 자산은 별도로 처리해야 할 때가 많습니다. 하지만 domi-indexed-sqlite는 WASM 자산을 직접 번들링하는 대신, sqlite-wasm의 기본 로딩 방식에 의존하거나 CDN 폴백을 사용하도록 설계되어 있었습니다. 따라서 tsup 설정 자체를 변경하는 것은 라이브러리의 설계 의도와 맞지 않았습니다.
다음으로, 경로 문제의 고전적인 원인인 상대 경로 계산 오류를 살펴보았습니다. 정적 배포 환경에서는 프로젝트가 웹 서버의 루트 경로(예: example.com/)가 아닌 서브 경로(예: example.com/my-repo/)에 배포되는 경우가 많습니다. 이때 JavaScript 코드 내부에서 ./asset.wasm과 같이 상대 경로로 자산을 요청하면, 브라우저는 이를 현재 페이지의 URL을 기준으로 해석합니다. 만약 페이지가 example.com/my-repo/index.html이라면 ./asset.wasm은 example.com/my-repo/asset.wasm을 요청해야 합니다. 하지만 때로는 웹 서버의 설정이나 SPA(Single Page Application) 라우팅 방식에 따라 /asset.wasm (루트 기준)을 요청해 버리는 경우가 발생합니다. 이 경우에도 404 에러가 발생합니다.
원인 좁히기: Emscripten과 WASM 자산 로딩
sqlite-wasm은 Emscripten으로 컴파일된 WASM 모듈입니다. Emscripten으로 생성된 JavaScript 글루 코드(glue code)는 .wasm 바이너리 파일과 함께 웹 워커 파일(예: sqlite3-worker1.js) 등 추가 자산들을 동적으로 로드해야 합니다. 이때 Emscripten은 Module.locateFile이라는 전역 함수를 통해 이러한 자산의 경로를 결정합니다.
domi-indexed-sqlite 라이브러리가 sqlite-wasm을 래핑하고 있으므로, 이 라이브러리가 내부적으로 locateFile을 어떻게 설정하는지가 핵심입니다. README에 명시된 CDN 폴백 탑재 기능은 이러한 자산 로딩 문제를 추상화하여 사용자가 별도의 설정을 하지 않아도 되도록 설계된 것이 분명했습니다. 하지만 정적 배포 환경에서 404 에러가 발생했다는 것은 이 폴백 로직이 제대로 작동하지 않았거나, 로컬 자산 로딩 경로를 결정하는 로직에 문제가 있었다는 의미입니다.
네트워크 탭을 자세히 확인해 보니, 라이브러리는 https://sqlite.org/sqlite-wasm/sqlite3.wasm (CDN 경로) 대신, 배포된 웹사이트의 상대 경로인 https://[내-도메인]/sqlite3.wasm (또는 https://[내-도메인]/my-repo/sqlite3.wasm이 아닌 https://[내-도메인]/sqlite3.wasm)을 먼저 시도하고 있었습니다. 문제는 domi-indexed-sqlite가 WASM 자산을 public 디렉토리 등에 복사하여 정적 파일로 제공하는 것이 아니라, sqlite-wasm이 설치된 node_modules 내부의 경로를 기준으로 상대 경로를 추론하려 했던 것으로 보입니다. 이 방식은 개발 환경에서는 빌드 도구의 가상 파일 시스템 덕분에 작동할 수 있지만, 정적 배포 환경에서는 해당 파일들이 웹 서버의 루트 경로에 없으므로 실패합니다.
해결: WASM 자산 경로 명시 및 CDN 폴백 강화
문제의 원인을 파악한 후, domi-indexed-sqlite 라이브러리 내부에서 sqlite-wasm 모듈을 초기화할 때 Module.locateFile 함수를 더욱 견고하게 구성해야 함을 깨달았습니다. 구체적인 해결책은 다음과 같습니다.
- WASM 자산의 기본 경로 설정: 라이브러리 초기화 시, WASM 자산이 위치할 수 있는
baseURL또는wasmAssetsUrl과 같은 옵션을initEasySqlite함수에 추가하여 사용자가 명시적으로 경로를 지정할 수 있도록 합니다. 예를 들어,initEasySqlite({ wasmAssetsUrl: 'https://cdn.example.com/sqlite-wasm/' })와 같이 설정하여 모든 WASM 관련 파일을 해당 URL에서 로드하도록 강제할 수 있습니다. 이는 특히 CDN 폴백이 작동하지 않을 때 유용합니다. - CDN 폴백 로직 강화: 라이브러리 내부에서
locateFile을 구현할 때, 로컬 경로에서 파일을 찾지 못하면 즉시 미리 정의된 CDN 경로로 폴백하도록 로직을 강화했습니다. 이는try-catch블록이나Promise.any와 같은 패턴을 사용하여 첫 번째 성공적인 로드를 반환하도록 구현할 수 있습니다.
이러한 변경을 통해, domi-indexed-sqlite는 이제 정적 배포 환경에서도 sqlite-wasm 자산을 올바르게 로드할 수 있게 되었습니다. 사용자는 라이브러리 초기화 시 wasmAssetsUrl 옵션을 통해 직접 경로를 제어하거나, 라이브러리의 견고해진 CDN 폴백 메커니즘에 의존할 수 있게 되었습니다.
import { initEasySqlite } from "domi-indexed-sqlite";
const db = await initEasySqlite({
dbName: "my_app_database",
debug: true,
// 정적 배포 환경에서 WASM 자산 경로가 어긋날 경우,
// 다음과 같이 명시적인 CDN 경로를 지정하여 해결할 수 있습니다.
// wasmAssetsUrl: 'https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@latest/dist/',
});
// ...이후 사용법은 README와 동일
다음에 할 것
현재는 명시적인 경로 설정이나 강화된 CDN 폴백으로 문제를 해결했지만, 향후에는 빌드 시스템(예: tsup)과 통합하여 WASM 자산을 자동으로 public 디렉토리로 복사하고, 라이브러리가 이 복사된 자산의 상대 경로를 자동으로 감지할 수 있도록 하는 메커니니즘을 추가하는 것을 고려하고 있습니다. 이렇게 하면 사용자가 별도로 wasmAssetsUrl을 설정할 필요 없이 모든 환경에서 domi-indexed-sqlite를 더 쉽게 사용할 수 있을 것입니다.