본문 바로가기

2026년 @capacitor-community/sqlite 대안

@capacitor-community/sqlite 대안을 찾고 계신가요? Capgo Fast SQL과 비교하고, Capacitor SQLite code와 사용자 데이터를 단계별로 마이그레이션하세요.

Capacitor 커뮤니티 SQLite 플러그인 대안 가이드용 Capgo 로고

주요 대안은 @capacitor-community/sqlite 대신 Capacitor 8에서 사용할 수 있는 @capgo/capacitor-fast-sql: iOS, Android, 웹을 위한 네이티브 SQLite 플러그인으로, SQLCipher는 선택 사항으로 두고 쿼리 데이터를 Capacitor bridge 대신 로컬 HTTP 채널로 전송합니다. 이 가이드는 두 가지를 솔직하게 비교하고, 언제 커뮤니티 플러그인에 남아야 하는지, 그리고 code과 사용자의 기존 데이터베이스를 모두 마이그레이션하는 방법을 다룹니다.

팀들이 대안을 찾는 이유

@capacitor-community/sqlite 는 수년간 Capacitor의 기본 SQLite 플러그인이었고 지금도 유지 관리되고 있습니다 (8.1.1은 2026년 8월에 출시). 사람들이 옮겨가는 이유는 구체적입니다:

  1. bridge 직렬화. 모든 결과가 JSON으로 Capacitor bridge를 거칩니다. 작은 쿼리에는 괜찮지만, 동기화나 검색을 위해 수천 개의 행을 로드할 때는 체감됩니다.
  2. SQLCipher는 항상 포함됩니다. 암호화를 전혀 쓰지 않아도 이 플러그인은 iOS와 Android에서 SQLCipher에 의존합니다. 그러면 네이티브 크기가 늘고, 바이너리에 암호화 code가 들어가므로 수출 규정 준수 답변에서 이를 고려해야 합니다.
  3. 웹 스토리지 모델. 웹에서는 다음 컴포넌트를 사용합니다: jeep-sqlite sql.js를 메모리에 올리고, 호출할 때 IndexedDB에 저장됩니다 saveToStore(). 저장을 잊지 말아야 하며, 데이터베이스 전체가 메모리에 상주합니다.
  4. API 표면이 넓습니다. 커넥션 래퍼, 일관성 검사, 읽기 전용 및 비준수 커넥션, 테이블 동기화, JSON 내보내기/가져오기. 강력하지만 'SQL 실행'만 하려면 배울 것이 많습니다.
  5. 앱당 패스프레이즈 1개. 암호화는 플러그인이 저장하는 단일 시크릿(setEncryptionSecret)을 앱 내 모든 암호화 데이터베이스가 공유합니다.

나란히 비교

@capacitor-community/sqlite 8.1 @capgo/capacitor-fast-sql 8.2
플랫폼 iOS, Android, Web, Electron iOS, Android, Web
데이터 경로 Capacitor 브리지 커넥션별 토큰을 사용하는 로컬 HTTP 서버 (네이티브)
웹 엔진 jeep-sqlite (sql.js + IndexedDB, 수동) saveToStore) 공식 @sqlite.org/sqlite-wasm OPFS 사용 시 파일에 기록
암호화 SQLCipher 항상 포함, 플러그인이 관리하는 패스프레이즈 하나 선택적 SQLCipher, 열 때 데이터베이스별 키 전달 connect()
Swift Package Manager 예 (Package.swift) 예 (Package.swift)
스키마 버전 관리 upgradeStatements / addUpgradeStatement 보통 직접 구현 PRAGMA user_version
JSON 가져오기/내보내기, 테이블 동기화 지원 아니요
에셋에서 미리 채워진 데이터베이스 copyFromAssets() 먼저 직접 파일을 복사하세요 connect()
Key-value 헬퍼 아니요 KeyValueStore
BLOB 지원 네, ~으로 Uint8Array
ORM 지원 TypeORM 내장 capacitor 드라이버 커스텀 드라이버 (Kysely 권장)
추가 Native 설정 Web: 복사 sql-wasm.wasm iOS NSAllowsLocalNetworking, Android localhost cleartext 규칙
메인테이너 Capacitor 커뮤니티 Capgo

커뮤니티 플러그인을 계속 써야 할 때

실용적으로 판단하세요. 유지할 때:

  • 출시한다면 Electron 빌드,
  • 사용한다면 TypeORM (capacitor 드라이버가 커뮤니티 플러그인을 기준으로 작성됨, 참고: Capacitor를 사용하는 TypeORM),
  • 의존하고 있다면 JSON import/export 또는 sync 테이블 그리고 이를 다시 빌드하고 싶지 않다면,
  • 쿼리가 작고 앱이 지금 잘 동작한다면.

Fast SQL이 더 적합한 경우

전환할 때:

  • 대규모 데이터셋을 로드하거나 쓴다면 (동기화 엔진, 오프라인 카탈로그, 검색 인덱스, CRDT 로그),
  • 일부 데이터베이스에만 암호화를 적용하고, Keychain 또는 Keystore에서 키를 가져오길 원할 때
  • 필요하지 않은 앱에 SQLCipher를 포함하고 싶지 않을 때
  • 인메모리 복사본이 아닌 OPFS를 통해 웹에서 실제 SQLite 파일을 사용하길 원할 때
  • 간단한 API를 원할 때: 연결, 쿼리, 실행, 트랜잭션.

Fast SQL 자체에 대한 전체 가이드는 Capacitor 앱에서 SQLite를 사용하는 방법.

1단계: 기존 플러그인과 함께 Fast SQL 설치

기존 데이터를 마이그레이션할 수 있도록 커뮤니티 플러그인을 한 릴리스 동안 유지하세요.

bun add @capgo/capacitor-fast-sql
bunx cap sync

Fast SQL에 필요한 네이티브 설정:

  • iOS: NSAppTransportSecurity > NSAllowsLocalNetworking = true 에서 Info.plist.
  • Android: network_security_config.xml cleartext를 허용하는 localhost 및 127.0.0.1, 매니페스트에서 참조됨.
  • 암호화: net.zetetic:sqlcipher-android Android 앱 모듈과 CapgoCapacitorFastSql/SQLCipher iOS pod에 있습니다. 자세한 내용은 Capacitor에서 SQLite 암호화.
  • 웹: Cross-Origin-Opener-Policy: same-origin 및 Cross-Origin-Embedder-Policy: require-corp OPFS용 헤더.

2단계: API 매핑

데이터베이스 열기

변경 전:

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';

const sqlite = new SQLiteConnection(CapacitorSQLite);
const db = await sqlite.createConnection('app', false, 'no-encryption', 1, false);
await db.open();

변경 후:

import { FastSQL } from '@capgo/capacitor-fast-sql';

const db = await FastSQL.connect({ database: 'app', walMode: true, performancePresets: true });

FastSQL.connect() 데이터베이스가 이미 열려 있으면 기존 연결을 반환하므로 별도의 과정이 isConnection / retrieveConnection 필요 없습니다.

메서드 매핑

커뮤니티 플러그인 빠른 SQL
createConnection() + open() FastSQL.connect({ database })
closeConnection(name) FastSQL.disconnect(name)
db.query(sql, values) 반환합니다 { values } db.query(sql, params) 행을 반환합니다
db.run(sql, values) 반환합니다 { changes: { changes, lastId } } db.run(sql, params) 반환합니다 { rowsAffected, insertId }
db.execute(multiStatementString) 하나 db.execute(sql) 문서당, 또는 db.executeBatch([...])
db.executeSet(set) db.executeBatch(ops) 포함 { statement, params }
beginTransaction / commitTransaction / rollbackTransaction db.transaction(async (tx) => ...) 또는 beginTransaction() / commit() / rollback()
addUpgradeStatement / upgradeStatements 어떤 PRAGMA user_version 마이그레이션 러너
copyFromAssets() 번들된 항목 복사 .db 앱으로 보내도록 Directory.Data 처음 실행 전 connect()
setEncryptionSecret() + mode: 'secret' encrypted: true, encryptionKey 켜짐 connect()
initWebStore() / saveToStore() 불필요, OPFS가 쓰기 내용을 유지

쿼리와 쓰기

변경 전:

const res = await db.query('SELECT * FROM notes WHERE pinned = ?', [1]);
const notes = res.values ?? [];

const { changes } = await db.run('INSERT INTO notes (title) VALUES (?)', ['Hello']);
const id = changes?.lastId;

변경 후:

const notes = await db.query('SELECT * FROM notes WHERE pinned = ?', [1]);

const { insertId } = await db.run('INSERT INTO notes (title) VALUES (?)', ['Hello']);

두 플러그인 모두 컬럼명을 키로 하는 객체로 행을 반환합니다.

멀티 스테이트먼트 실행

커뮤니티 플러그인은 execute() 세미콜론으로 구분된 여러 SQL 문이 포함된 문자열을 ;받아들입니다. Fast SQL은 호출당 하나의 SQL 문만 준비합니다. SQL을 나누거나, 더 나은 방법은 SQL 문을 배열로 유지하세요:

const schema = [
  'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)',
  'CREATE INDEX IF NOT EXISTS idx_notes_title ON notes (title)',
];

await db.executeBatch(schema.map((statement) => ({ statement })));

다음 기준으로 분리하지 마세요: ; 단순한 방식으로 split(';') SQL 문에 트리거 본문이나 세미콜론이 포함된 문자열 리터럴이 있으면

user_version으로 업그레이드 구문

커뮤니티 플러그인도 스키마 버전을 PRAGMA user_version 에 저장합니다. 버전 번호를 그대로 유지하면 3단계 파일 마이그레이션 후 기존 사용자 데이터베이스는 이미 올바른 버전에 있습니다.

import type { SQLConnection } from '@capgo/capacitor-fast-sql';

// Same content as your old upgrade statements, index 0 = version 1
const versions: string[][] = [
  ['CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)'],
  ['ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0'],
];

export async function migrate(db: SQLConnection) {
  const [{ user_version }] = await db.query('PRAGMA user_version');
  for (let v = Number(user_version); v < versions.length; v++) {
    await db.transaction(async (tx) => {
      for (const statement of versions[v]) await tx.execute(statement);
      await tx.execute(`PRAGMA user_version = ${v + 1}`);
    });
  }
}

이 방식을 신뢰하기 전에 기존 설치 환경에서 실제 user_version 을 먼저 확인하세요. 확실하지 않다면 현재 릴리스의 이전 플러그인에서 로그를 남기세요.

미리 채워진 데이터베이스

커뮤니티 플러그인의 copyFromAssets() 는 첫 실행 시 번들된 데이터베이스를 복사합니다. Fast SQL을 사용할 때는 연결하기 전에 플러그인이 찾는 위치에 파일을 두세요. Fast SQL은 <name>.db 에서 Documents iOS 및 앱의 files 디렉토리에 저장하며, 두 곳 모두 Directory.Data 에서 @capacitor/filesystem:

import { Directory, Filesystem } from '@capacitor/filesystem';
import { FastSQL } from '@capgo/capacitor-fast-sql';

async function openCatalog() {
  const exists = await Filesystem.stat({ path: 'catalog.db', directory: Directory.Data })
    .then(() => true)
    .catch(() => false);

  if (!exists) {
    const blob = await (await fetch('/assets/catalog.db')).blob();
    const base64 = await new Promise<string>((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
      reader.readAsDataURL(blob);
    });
    await Filesystem.writeFile({ path: 'catalog.db', data: base64, directory: Directory.Data });
  }
  return FastSQL.connect({ database: 'catalog', readOnly: true });
}

대용량 파일의 경우 청크 단위로 다운로드하거나 압축 파일을 배포한 뒤, JavaScript의 base64 대신 파일 플러그인의 native copy를 사용하세요.

3단계: 기존 사용자 데이터 마이그레이션

앱을 업데이트한 사용자는 커뮤니티 플러그인이 만든 데이터베이스를 가지고 있습니다. 이 데이터베이스는 다른 경로에 다른 이름으로 존재합니다:

커뮤니티 플러그인 빠른 SQL
iOS Documents/<name>SQLite.db (기본값 iosDatabaseLocation) Documents/<name>.db
Android databases/<name>SQLite.db files/<name>.db

옵션 A: 파일 복사 (암호화되지 않은 데이터베이스)

가장 빠르며 인덱스, 트리거 등 모든 것을 유지합니다: user_version.

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
import { Directory, Filesystem } from '@capacitor/filesystem';
import { Preferences } from '@capacitor/preferences';

export async function moveCommunityDatabase(name: string): Promise<void> {
  const done = await Preferences.get({ key: `sqlite-moved:${name}` });
  if (done.value === '1') return;

  const sqlite = new SQLiteConnection(CapacitorSQLite);
  const exists = (await sqlite.isDatabase(name)).result;
  if (exists) {
    const conn = await sqlite.createConnection(name, false, 'no-encryption', 1, false);
    await conn.open();
    await conn.execute('PRAGMA wal_checkpoint(TRUNCATE)', false).catch(() => undefined);
    const { url } = await conn.getUrl();
    await sqlite.closeConnection(name, false);

    if (url) {
      await Filesystem.copy({ from: url, to: `${name}.db`, toDirectory: Directory.Data });
    }
  }
  await Preferences.set({ key: `sqlite-moved:${name}`, value: '1' });
}

첫 실행 전에 호출하세요 FastSQL.connect({ database: name }). 참고 사항:

  • getUrl() (iOS 및 Android) 데이터베이스 파일 URL을 반환합니다. 전체 경로를 전달하면 file:// URL에 없음 directory Filesystem 플러그인이 샌드박스 내 모든 경로에서 읽을 수 있도록 합니다.
  • 연결을 닫고 먼저 체크포인트를 찍으면 최근 쓰기가 -wal 다운로드됩니다.
  • 기존 파일을 한 릴리스 동안 유지한 뒤 커뮤니티 연결의 delete() 메서드로 확신이 들면 삭제하세요. 그런 다음 커뮤니티 플러그인을 제거합니다.

옵션 B: JavaScript에서 행 복사 (암호화 데이터베이스 또는 스키마 변경)

커뮤니티 플러그인의 저장된 secret으로 소스가 암호화되어 있다면, 가장 간단하고 안전한 방법은 해당 플러그인이 복호화한 뒤 새로운 Fast SQL 데이터베이스로 행을 복사하는 것입니다. 이 방식을 사용하면 secure storage에 있는 데이터베이스별 새 키로 암호화할 수도 있습니다.

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
import { FastSQL } from '@capgo/capacitor-fast-sql';
import { Preferences } from '@capacitor/preferences';

export async function copyRows(name: string, target: { encryptionKey?: string }) {
  // Record which destination mode was migrated, so a later call with a different target fails loudly
  const mode = target.encryptionKey ? 'encrypted' : 'plain';
  const done = await Preferences.get({ key: `sqlite-copied:${name}` });
  if (done.value === mode) return;
  if (done.value) throw new Error(`${name} was already copied as ${done.value}, not ${mode}`);

  const sqlite = new SQLiteConnection(CapacitorSQLite);
  const src = await sqlite.createConnection(name, true, 'secret', 1, false);
  await src.open();

  try {
    const dst = await FastSQL.connect({
      database: name,
      encrypted: Boolean(target.encryptionKey),
      encryptionKey: target.encryptionKey,
    });

    const objects = ((await src.query(
      `SELECT type, name, sql FROM sqlite_master
       WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'`,
    )).values ?? []) as { type: string; name: string; sql: string }[];
    const tables = objects.filter((o) => o.type === 'table');
    const others = objects.filter((o) => o.type !== 'table');
    const version = (await src.query('PRAGMA user_version')).values?.[0]?.user_version ?? 0;

    await dst.transaction(async (tx) => {
      for (const t of tables) await tx.execute(t.sql);
      for (const t of tables) {
        const rows = (await src.query(`SELECT * FROM "${t.name}"`)).values ?? [];
        if (!rows.length) continue;
        const cols = Object.keys(rows[0]);
        const statement = `INSERT INTO "${t.name}" (${cols.map((c) => `"${c}"`).join(', ')}) VALUES (${cols.map(() => '?').join(', ')})`;
        await tx.executeBatch(rows.map((r) => ({ statement, params: cols.map((c) => r[c]) })));
      }
      for (const o of others) await tx.execute(o.sql);
      await tx.execute(`PRAGMA user_version = ${Number(version)}`);
    });

    // Only mark the copy as done once the transaction has committed
    await Preferences.set({ key: `sqlite-copied:${name}`, value: mode });
  } finally {
    await sqlite.closeConnection(name, false);
  }
}

사용 'no-encryption' 및 false 에서 createConnection 소스가 암호화되지 않은 경우입니다. 커뮤니티 플러그인에서 요구하는 것은 iosIsEncryption / androidIsEncryption 에서 capacitor.config 암호화 모드에 필요하며, 해당 모드를 사용했다면 이미 갖고 있습니다. 대용량 테이블은 다음을 사용해 페이지 단위로 조회하세요. WHERE rowid > ? ORDER BY rowid LIMIT 5000.

두 플러그인 모두 SQLCipher 4 기본 설정을 사용하므로, 암호화된 파일을 동일한 passphrase로 Fast SQL에서 직접 열 수도 있습니다. 다만 커뮤니티 플러그인은 해당 passphrase를 내부적으로 관리하므로, 행 복사 방식을 사용하면 이러한 세부 사항에 의존하지 않아도 됩니다.

4단계: 기존 플러그인 제거

마이그레이션이 적용된 상태로 한두 번 릴리스한 후:

bun remove @capacitor-community/sqlite jeep-sqlite
bunx cap sync

제거 sql-wasm.wasm assets에서 그리고 모든 jeep-sqlite 설정 시간 main.ts. 플러그인을 제거하면 SQLCipher 의존성도 함께 제거되므로, Fast SQL의 암호화를 사용하지 않으면 iOS와 Android 바이너리 크기가 줄어듭니다.

데이터 마이그레이션 code 은 JavaScript이므로 다음과 함께 배포할 수 있습니다 Capgo Live Update 사용하기, 하지만 Fast SQL을 추가하는 릴리스는 네이티브 변경 사항이므로 스토어 빌드가 필요합니다. 먼저 네이티브 빌드를 배포하세요. Capgo 빌드 Mac을 상시 보유하지 않는 경우 클라우드에서 빌드를 생성할 수 있습니다(App Store 업로드는 2026년 4월부터 Xcode 26 이상 필요).

문제 해결

Fast SQL은 연결되지만 iOS에서 모든 쿼리가 실패합니다. NSAllowsLocalNetworking 앱의 Info.plist에 없거나, 추가 후 앱이 다시 빌드되지 않았습니다.

Android: CLEARTEXT communication to localhost not permitted. 로컬호스트 추가 domain-config 네트워크 보안 설정에 추가하세요. SSL 핀닝용 설정이 이미 있다면 병합하세요.

스키마의 일부만 생성되었습니다. 여러 문장이 포함된 문자열을 전달했습니다. execute()배열로 분리하세요.

INSERT ... RETURNING Android에서 아무것도 반환되지 않습니다. Fast SQL 8.2에서 Android는 다음으로 시작하는 문장에 대해서만 행을 반환합니다. SELECT, PRAGMA 또는 EXPLAIN. 사용하세요 insertId 과 run().

Android에서 읽을 때 불리언 값이 일치하지 않습니다. 전달하세요: 1/0 를 전달하고 true/false 쿼리 파라미터에 포함됩니다. Android는 읽기 전용 쿼리 파라미터를 텍스트로 바인딩합니다.

복사된 데이터베이스가 비어 있습니다. 커뮤니티 플러그인이 커스텀 iosDatabaseLocation으로 설정되었거나, 소스 이름이 다릅니다. 반환된 URL을 로그로 기록하세요. getUrl().

기존 플러그인의 웹 데이터는 사라졌습니다. 커뮤니티 플러그인은 IndexedDB를 통해 웹 데이터를 저장했지만, jeep-sqliteFast SQL은 OPFS를 사용합니다. 백엔드에서 웹 클라이언트를 다시 동기화하거나, 기존 플러그인으로 exportToJson 데이터를 내보낸 후 Fast SQL로 행을 가져오세요.

다른 옵션

  • @capgo/capacitor-data-storage-sqlite SQLite 기반의 문자열 키-값 저장소가 꼭 필요하다면 다음을 확인하세요. Capacitor의 키-값 저장소.
  • 그 위에 ORM 또는 쿼리 빌더: Kysely 은 간단한 커스텀 드라이버를 통해 Fast SQL과 연동됩니다. Drizzle 은 현재 커뮤니티 플러그인에 더 적합합니다.

전체 Fast SQL API는 다음에서 확인하세요: 플러그인 문서 그리고 플러그인 페이지.

Capacitor 앱용 Live updates

웹 레이어 버그가 라이브 상태라면, 앱 스토어 승인을 며칠씩 기다리지 말고 Capgo를 통해 수정 사항을 배포하세요. 네이티브 변경 사항은 정상 리뷰 절차를 그대로 따르는 반면, 사용자는 백그라운드에서 업데이트를 받습니다.

블로그 최신 글

Capgo는 정말 프로페셔널한 모바일 앱을 만드는 데 필요한 최고의 인사이트를 제공합니다.