codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›frontend

SQLite — 로컬 앱의 단일 파일 DB

2026-04-28 게시· 2026-05-18 갱신·0회 조회

SQLite — 로컬 앱의 단일 파일 DB

SQLite 는 단일 파일에 SQL 데이터베이스를 담는 임베디드 DB 입니다. 데스크탑·모바일 앱의 로컬 저장소로 가장 자주 쓰이는 선택지 중 하나입니다.

1. SQLite 에 대한 이야기

SQLite 는 D. Richard Hipp 이 2000 년 5 월 첫 릴리스한 라이브러리입니다. C 로 쓰여 있고 퍼블릭 도메인 (저작권을 의도적으로 포기) 으로 배포됩니다. 기여를 받기 위해 별도 CLA 절차가 있습니다.

공식 사이트는 자기 소개에서 "the most used database engine in the world" 라 표현합니다. iOS · Android · Windows 10 · macOS · 대부분의 브라우저 · 항공기 시스템 · 자동차 · 휴대전화에 내장돼 있다는 점이 근거로 거론됩니다.

라이선스가 퍼블릭 도메인이라 배포에 제약이 거의 없습니다. 상용 지원이 필요한 조직을 위해 SQLite Consortium · 유료 라이선스 옵션이 별도로 존재합니다.

2. 단일 파일 모델

DB 한 개 = 파일 하나 (my.db). 그 안에 모든 테이블·인덱스·트리거가 들어갑니다. 프로세스가 별도로 뜨는 클라이언트-서버 모델 (PostgreSQL · MySQL) 과 다르게 라이브러리가 호스트 프로세스 안에서 동작합니다.

이 모델의 결과:

  • 설정이 거의 없음 — 호스트가 파일을 열기만 하면 됩니다.
  • 백업이 단순함 — 파일을 복사합니다 (다만 라이브 DB 는 잠금 고려가 필요).
  • 네트워크 지연 없음 — 같은 프로세스 안 함수 호출.
  • 여러 프로세스에서 쓰기 — 가능은 하지만 락 경합이 있습니다. 단일 라이터 + 다중 리더 패턴이 가장 자연스럽습니다.

3. 저널 모드와 WAL

쓰기 도중 충돌해도 일관성을 유지하기 위해 SQLite 는 저널을 씁니다. 두 모드가 흔합니다.

  • rollback journal (기본) — 변경 전 페이지를 별도 파일에 백업. 트랜잭션 완료 후 삭제.
  • WAL (Write-Ahead Logging, 3.7.0 / 2010) — 변경을 별도 .db-wal 에 추가하고 주기적으로 본 파일에 합칩니다 (checkpoint).

WAL 의 이점:

  • 읽기와 쓰기가 동시에 가능 (읽기는 WAL 이 적용되기 전 스냅샷을 봄).
  • 일반적으로 더 빠른 쓰기.

활성화는 한 줄:

PRAGMA journal_mode = WAL;

WAL 은 같은 파일 시스템 위의 동기화 정확성에 의존합니다. 일부 네트워크 파일 시스템 (NFS) 에서는 권장되지 않습니다 (공식 문서 명시).

4. 동시성과 타입

SQLite 의 동시성 모델은 단순합니다.

  • 공유 락 (SHARED) — 여러 리더 동시.
  • 예약 락 (RESERVED) — 다음 라이터 예약.
  • 배타 락 (EXCLUSIVE) — 실제 쓰기.

WAL 모드에서는 리더와 라이터가 같은 락을 두고 다투지 않아 동시성이 더 좋습니다.

SQLite 는 type affinity 라는 느슨한 타입 모델을 가집니다. 컬럼 타입을 적어도 다른 타입의 값이 저장될 수 있습니다.

CREATE TABLE t (id INTEGER, name TEXT);
INSERT INTO t VALUES ("hello", 42);  -- 통과

3.37.0 (2021) 부터 STRICT 옵션이 추가되어 엄격한 타입 검사를 켤 수 있습니다.

CREATE TABLE t (id INTEGER, name TEXT) STRICT;

5. 확장 기능

  • JSON1 — 내장. JSON 값을 저장·인덱스·쿼리.
  • FTS5 — Full-text search.
  • R*Tree — 공간 인덱스.
  • vector / sqlite-vec — 벡터 검색 (외부 확장, 2024).

이 모든 것이 단일 파일 안에서 동작합니다.

6. Tauri 와의 통합

Tauri 에서 SQLite 를 쓰는 길은 두 갈래입니다.

tauri-plugin-sql — 공식 플러그인. Rust 의 sqlx 를 통해 SQLite/MySQL/Postgres 를 같은 인터페이스로 사용. JS 에서 SQL 문자열을 보냅니다.

import Database from "@tauri-apps/plugin-sql"

const db = await Database.load("sqlite:app.db")
await db.execute("CREATE TABLE IF NOT EXISTS food (id INTEGER PRIMARY KEY, name TEXT)")
await db.execute("INSERT INTO food (name) VALUES ($1)", ["김치"])
const rows = await db.select<{id: number; name: string}[]>("SELECT * FROM food")

rusqlite 직접 — Rust 코드 안에서만 SQLite 를 만지고 JS 에는 도메인 함수만 노출. 타입 안전성·성능이 더 좋지만 코드량이 늘어납니다.

#[tauri::command]
fn add_food(name: String, db: tauri::State<DbPool>) -> Result<i64, String> {
    let conn = db.0.get().map_err(|e| e.to_string())?;
    conn.execute("INSERT INTO food (name) VALUES (?1)", [&name])
        .map_err(|e| e.to_string())?;
    Ok(conn.last_insert_rowid())
}

7. 파일 위치 관습

각 OS 의 사용자 데이터 디렉토리에 두는 것이 표준입니다.

  • Windows: %APPDATA%\<app-id>\ (C:\Users\Alice\AppData\Roaming\com.example.app\)
  • macOS: ~/Library/Application Support/<app-id>/
  • Linux: ~/.local/share/<app-id>/ (XDG)
  • Android: /data/data/<package>/files/
  • iOS: 앱 샌드박스의 Library/Application Support/

Tauri 의 app.path().app_data_dir() 가 이를 추상화합니다.

8. 백업·동기화 패턴

라이브 DB 파일을 단순 복사하면 트랜잭션 도중일 수 있어 손상 위험이 있습니다. 안전한 길:

  • VACUUM INTO 'backup.db' — 라이브 DB 의 일관 사본을 만듭니다.
  • 백업 API (sqlite3_backup_*) — 라이브 DB 를 다른 DB 로 페이지 단위 복사.

9. 브라우저·로컬 저장 후보들

저장소 위치 용량 특징
localStorage 브라우저 약 5~10 MB 동기 API · 문자열만 · 작은 설정.
sessionStorage 브라우저 비슷 탭 단위 수명.
IndexedDB 브라우저 수백 MB~수 GB (쿼터) 비동기 · 객체 저장 · 인덱스. 쿼리 표현 빈약.
OPFS 브라우저 큼 파일 단위 API. WASM SQLite 와 결합 가능.
WASM SQLite 브라우저 OPFS 위 공식. 브라우저 안에서 진짜 SQLite.
SQLite (네이티브 앱) 호스트 파일 시스템 디스크 한도 본 글의 주제.
Dexie.js IndexedDB 위 친숙한 API 래퍼.

웹 앱이라면 IndexedDB 또는 WASM SQLite, 데스크탑·모바일이라면 네이티브 SQLite 가 자연스러운 선택입니다.

10. 권장 PRAGMA

PRAGMA journal_mode = WAL;
PRAGMA synchronous  = NORMAL;
PRAGMA foreign_keys = ON;
PRAGMA temp_store   = MEMORY;

synchronous = NORMAL 은 성능과 내구성의 절충입니다. 금융 데이터 같은 자리는 FULL 이 더 안전합니다.

11. 자주 걸리는 자리

여러 프로세스 쓰기 — 한 시점에 라이터는 하나입니다. 다른 프로세스가 쓰는 동안 시도하면 SQLITE_BUSY. busy_timeout 또는 큐로 직렬화합니다.

스레드 안전성 — SQLite 는 컴파일 옵션에 따라 thread-safe 모드가 다릅니다. rusqlite 는 connection 객체를 스레드 간 공유하지 못하도록 강제합니다 (Send + !Sync). 풀 (r2d2) 또는 채널을 거칩니다.

WAL 파일 동반 — db.db 를 백업할 때 db.db-wal · db.db-shm 도 함께 가야 합니다. VACUUM INTO 를 써서 단일 파일로 만드는 편이 안전합니다.

네트워크 파일 시스템 — NFS 같은 환경은 락 의미가 다릅니다. 손상이 보고되며 공식적으로 비권장입니다.

타입의 느슨함 — STRICT 없이 만든 테이블은 잘못된 타입의 값이 들어가도 통과합니다. 새 테이블은 STRICT 권장.

공유 디렉토리 — 클라우드 동기화 폴더 (OneDrive · iCloud · Dropbox) 안에 라이브 DB 를 두면 동기화가 락과 충돌해 손상 위험이 있습니다.

이모지·서로게이트 — SQLite 는 UTF-8 을 다루지만 BMP 외 문자 (이모지) 가 잘못된 위치에서 잘리면 손상으로 이어질 수 있습니다. 입력 단계에서 정규화합니다.

하고픈 말

SQLite 는 로컬 앱의 가장 단순한 답입니다. 단일 파일 + 표준 SQL + 퍼블릭 도메인 라이선스 셋이 다른 후보를 압도합니다. WAL 모드 + STRICT 테이블 + foreign_keys ON 만 처음에 켜면 운영 부담이 거의 없습니다.

Next

  • ocr-stt-tts
  • native-integrations

SQLite 공식 · SQLite About · Most Widely Deployed · WAL Mode · Datatype In SQLite · tauri-plugin-sql · rusqlite · sqlx · SQLite WASM · Dexie.js 를 참고합니다.

frontend 카테고리의 다른 글

카테고리 전체 보기 →
  • 도메인 위젯의 통일성 — 4개 도메인에 3개 위젯만 두지 마라
  • 관리자 UI — ResourceTable SSOT 패턴
  • 페이지 로딩 UX
  • 네이티브 통합 — OS 기능들
  • OCR · STT · TTS
  • Tauri 모바일과 AdMob