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

SQLite를 사용하여 웹에서 모든 저장소 요구사항을 효율적으로 처리하세요.

SQLite 정보

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

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

SQLite 로고

웹 어셈블리 기반의 SQLite

웹 어셈블리(Wasm)를 기반으로 하는 여러 비공식 SQLite 버전이 있으므로 웹브라우저에서 사용할 수 있습니다(예: sql.js). sqlite3 WASM/JS 하위 프로젝트는 지원되는 SQLite 결과물 제품군의 라이브러리로 구성된 Wasm 빌드를 만드는 SQLite 프로젝트와 공식적으로 연결된 첫 번째 작업입니다. 이 프로젝트의 구체적인 목표는 다음과 같습니다.

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

원본 비공개 파일 시스템 지속성 백엔드와 함께 SQLite Wasm 사용

npm에서 라이브러리 설치

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

npm install @sqlite.org/sqlite-wasm

원본 비공개 파일 시스템

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

아시다시피 프로젝트 목표의 마지막 요점인 사용 가능한 JavaScript API를 사용하는 영구 클라이언트 측 저장소 지원에는 데이터베이스 파일에 데이터를 유지하는 것과 관련하여 엄격한 성능 요구사항이 수반됩니다. 여기서 원본 비공개 파일 시스템, 더 구체적으로는 FileSystemFileHandle 객체의 createSyncAccessHandle() 메서드가 사용됩니다. 이 메서드는 파일을 동기적으로 읽고 쓰는 데 사용할 수 있는 FileSystemSyncAccessHandle 객체로 확인되는 프로미스를 반환합니다. 이 메서드의 동기식 특성은 성능상의 이점을 제공하지만 원본 비공개 파일 시스템 내의 파일에 대한 전용 웹 작업자 내에서만 사용할 수 있으므로 기본 스레드를 차단할 수 없습니다.

필수 헤더 설정

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

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

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

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

속도 테스트

SQLite팀은 지원 중단된 웹 SQL과 비교하여 WebAssembly 구현에 관한 벤치마크를 실행했습니다. 이러한 벤치마크에서는 SQLite Wasm이 일반적으로 웹 SQL만큼 빠른 것을 알 수 있습니다. 때로는 조금 느릴 수도 있고 더 빠르기도 합니다 결과 페이지에서 모든 세부정보를 확인하세요.

시작하기 코드 샘플

앞서 언급했듯이 출처 비공개 파일 시스템 지속성 백엔드가 있는 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);
  }
})();

데모

데모에서 위의 코드의 실제 동작을 확인하세요. Glitch에서 소스 코드를 확인하세요. 아래에 삽입된 버전은 OPFS 백엔드를 사용하지 않지만 별도 탭에서 데모를 열면 OPFS 백엔드를 사용한다는 점에 유의하세요.

원본 비공개 파일 시스템 디버깅

SQLite Wasm의 원본 비공개 파일 시스템 출력을 디버그하려면 OPFS Explorer Chrome 확장 프로그램을 사용하세요.

Chrome 웹 스토어의 OPFS Explorer

확장 프로그램을 설치한 후 Chrome DevTools를 열고 OPFS 탐색기 탭을 선택하면 SQLite Wasm이 원본 비공개 파일 시스템에 쓰는 내용을 검사할 수 있습니다.

데모 앱의 출처 비공개 파일 시스템 구조를 보여주는 OPFS Explorer Chrome 확장 프로그램

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

SQLite Wasm 데모에서 데이터베이스 파일을 여는 데 사용되는 SQLite 뷰어 앱입니다.

도움 요청 및 의견 제공

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

감사의 말

UnsplashTobias Fischer의 히어로 이미지