원본 비공개 파일 시스템에서 지원하는 브라우저에 있는 SQLite Wasm

SQLite를 사용하여 웹에서 모든 스토리지 요구사항을 성능이 우수하게 처리하세요.

SQLite는 널리 사용되는 오픈소스 경량 임베디드 관계형 데이터베이스 관리 시스템입니다. 많은 개발자가 구조화되고 사용하기 쉬운 방식으로 데이터를 저장하는 데 사용합니다. SQLite는 크기가 작고 메모리 요구사항이 낮기 때문에 모바일 기기, 데스크톱 애플리케이션, 웹브라우저에서 데이터베이스 엔진으로 자주 활용됩니다.

SQLite의 주요 기능 중 하나는 서버리스 데이터베이스라는 것입니다. 즉, 작동하는 데 별도의 서버 프로세스가 필요하지 않습니다. 대신 데이터베이스는 사용자의 기기에 있는 단일 파일에 저장되므로 애플리케이션에 쉽게 통합할 수 있습니다.

SQLite 로고

Web Assembly 기반 SQLite

Web Assembly(Wasm)를 기반으로 하는 비공식 SQLite 버전이 여러 개 있으며, 이를 통해 웹브라우저에서 사용할 수 있습니다(예: sql.js). sqlite3 WASM/JS 하위 프로젝트SQLite 프로젝트와 공식적으로 연결되어 라이브러리의 Wasm 빌드를 지원되는 SQLite 결과물의 가족 구성원으로 만드는 첫 번째 노력입니다. 이 프로젝트의 구체적인 목표는 다음과 같습니다.

  • 사용 측면에서 C에 최대한 가까운 하위 수준 sqlite3 API를 바인딩합니다.
  • 하위 수준 API에 직접 연결되는 상위 수준 객체 지향 API로, sql.jsNode.js 스타일 구현과 유사합니다. 이 API는 하위 수준 API와 동일한 스레드에서 사용해야 합니다.
  • 작업자 메시지를 통해 이전 API와 통신하는 작업자 기반 API 이 API는 기본 스레드에서 사용하기 위한 것으로, 하위 수준 API는 작업자 스레드에 설치되고 작업자 메시지를 통해 통신합니다.
  • 사용자에게 교차 스레드 통신 측면을 완전히 숨기는 Worker API의 Promise 기반 변형입니다.
  • Origin Private File System (OPFS)을 비롯한 사용 가능한 JavaScript API를 사용한 영구 클라이언트 측 저장소 지원

Origin Private File System 지속성 백엔드와 함께 SQLite Wasm 사용

npm에서 라이브러리 설치

다음 명령어를 사용하여 npm에서 @sqlite.org/sqlite-wasm 패키지를 설치합니다.

npm install @sqlite.org/sqlite-wasm

Origin Private File System

파일 시스템 액세스 API의 일부인 Origin Private File System (OPFS)은 데이터에 매우 효율적으로 액세스할 수 있는 특수 서페이스로 보강됩니다. 이 새로운 표면은 파일 콘텐츠에 대한 독점적인 인플레이스 쓰기 액세스를 제공한다는 점에서 기존 표면과 다릅니다. 이 변경사항은 플러시되지 않은 수정사항을 일관되게 읽는 기능과 전용 작업자에서 동기 변형을 사용할 수 있는 기능과 함께 성능을 크게 개선하고 새로운 사용 사례를 차단 해제합니다.

짐작하셨겠지만 프로젝트 목표의 마지막 항목인 사용 가능한 JavaScript API를 사용한 지속적인 클라이언트 측 저장소 지원에는 데이터베이스 파일에 데이터를 유지하는 것과 관련된 엄격한 성능 요구사항이 있습니다. 이때 Origin Private File System, 더 구체적으로는 FileSystemFileHandle 객체의 createSyncAccessHandle() 메서드가 사용됩니다. 이 메서드는 파일에서 동기식으로 읽고 쓸 수 있는 FileSystemSyncAccessHandle 객체로 확인되는 Promise를 반환합니다. 이 메서드의 동기적 특성은 성능 이점을 제공하지만, 따라서 기본 스레드가 차단되지 않도록 Origin Private File System 내 파일 전용 Web Worker 내에서만 사용할 수 있습니다.

필수 헤더 설정

다운로드된 SQLite Wasm 보관 파일에는 sqlite3 WASM/JS 빌드를 구성하는 sqlite3.jssqlite3.wasm 파일이 포함되어 있습니다. jswasm 디렉터리에는 핵심 sqlite3 결과물이 포함되고 최상위 디렉터리에는 데모 및 테스트 앱이 포함됩니다. 브라우저는 file:// URL에서 Wasm 파일을 제공하지 않으므로 이를 사용하여 빌드하는 앱에는 웹 서버가 필요하며 서버는 파일을 제공할 때 응답에 다음 헤더를 포함해야 합니다.

  • Cross-Origin-Opener-Policysame-origin 지시어로 설정됩니다. 이 지시어는 탐색 컨텍스트를 동일 출처 문서로만 분리합니다. 크로스 오리진 문서는 동일한 탐색 컨텍스트에 로드되지 않습니다.
  • Cross-Origin-Embedder-Policyrequire-corp 지시어로 설정되어 있으므로 문서는 동일한 출처의 리소스 또는 다른 출처에서 로드할 수 있다고 명시적으로 표시된 리소스만 로드할 수 있습니다.

이러한 헤더가 필요한 이유는 SQLite Wasm이 SharedArrayBuffer에 종속되어 있고 이러한 헤더를 설정하는 것이 보안 요구사항의 일부이기 때문입니다.

DevTools로 트래픽을 검사하면 다음 정보를 확인할 수 있습니다.

위에서 언급한 두 헤더인 Cross-Origin-Embedder-Policy와 Cross-Origin-Opener-Policy가 Chrome DevTools에서 강조 표시되어 있습니다.

Speedtest

SQLite팀은 지원이 중단된 Web SQL과 비교하여 WebAssembly 구현에 대한 몇 가지 벤치마크를 실행했습니다. 이러한 벤치마크는 SQLite Wasm이 일반적으로 Web SQL만큼 빠르다는 것을 보여줍니다. 때로는 조금 느리고 때로는 조금 빠릅니다. 결과 페이지에서 모든 세부정보를 확인하세요.

시작 코드 샘플

앞서 언급했듯이 Origin Private File System 지속성 백엔드가 있는 SQLite Wasm은 작업자 컨텍스트에서 실행해야 합니다. 좋은 소식은 라이브러리에서 이 모든 것을 자동으로 처리하므로 기본 스레드에서 바로 사용할 수 있다는 것입니다.

import { sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm';

(async () => {
  try {
    console.log('Loading and initializing SQLite3 module...');

    const promiser = await new Promise((resolve) => {
      const _promiser = sqlite3Worker1Promiser({
        onready: () => {
          resolve(_promiser);
        },
      });
    });

    console.log('Done initializing. Running demo...');

    let response;

    response = await promiser('config-get', {});
    console.log('Running SQLite3 version', response.result.version.libVersion);

    response = await promiser('open', {
      filename: 'file:worker-promiser.sqlite3?vfs=opfs',
    });
    const { dbId } = response;
    console.log(
      'OPFS is available, created persisted database at',
      response.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
    );

    await promiser('exec', { dbId, sql: 'CREATE TABLE IF NOT EXISTS t(a,b)' });
    console.log('Creating a table...');

    console.log('Insert some data using exec()...');
    for (let i = 20; i <= 25; ++i) {
      await promiser('exec', {
        dbId,
        sql: 'INSERT INTO t(a,b) VALUES (?,?)',
        bind: [i, i * 2],
      });
    }

    console.log('Query data with exec()');
    await promiser('exec', {
      dbId,
      sql: 'SELECT a FROM t ORDER BY a LIMIT 3',
      callback: (result) => {
        if (!result.row) {
          return;
        }
        console.log(result.row);
      },
    });

    await promiser('close', { dbId });
  } catch (err) {
    if (!(err instanceof Error)) {
      err = new Error(err.result.message);
    }
    console.error(err.name, err.message);
  }
})();

데모

데모에서 위의 코드를 확인하세요. GitHub에서 소스 코드를 확인하세요. 아래에 삽입된 버전은 OPFS 백엔드를 사용하지 않지만 별도의 탭에서 데모를 열면 OPFS 백엔드를 사용합니다.

Origin Private File System 디버그

SQLite Wasm의 Origin Private File System 출력을 디버그하려면 OPFS Explorer Chrome 확장 프로그램을 사용하세요.

Chrome 웹 스토어의 OPFS 탐색기

확장 프로그램을 설치한 후 Chrome DevTools를 열고 OPFS Explorer 탭을 선택하면 SQLite Wasm이 Origin Private File System에 쓰는 내용을 검사할 수 있습니다.

데모 앱의 Origin Private File System 구조를 보여주는 OPFS 탐색기 Chrome 확장 프로그램

DevTools의 OPFS 탐색기 창에서 파일을 선택하면 로컬 디스크에 저장할 수 있습니다. 그런 다음 SQLite Viewer와 같은 앱을 사용하여 데이터베이스를 검사하여 SQLite Wasm이 실제로 약속한 대로 작동하는지 확인할 수 있습니다.

SQLite Wasm 데모에서 데이터베이스 파일을 여는 데 사용되는 SQLite Viewer 앱

도움 받기 및 의견 제공

SQLite Wasm은 SQLite 커뮤니티에서 개발하고 유지관리합니다. 지원 포럼에서 검색하고 게시하여 도움을 받고 의견을 제공하세요. 전체 문서는 SQLite 사이트에서 확인할 수 있습니다.