핵심 정리
import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync("./data.db");
db.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)");
const insert = db.prepare("INSERT INTO users (name) VALUES (?)");
insert.run("kero");
const rows = db.prepare("SELECT * FROM users").all();
console.log(rows);
db.close();sqlite 사용
먼저 구분해 둘 기본형은 아래입니다.
- 파일 DB 연결:
new DatabaseSync("./data.db") - 메모리 DB:
new DatabaseSync(":memory:") - 스키마 준비:
db.exec(...) - 안전한 쿼리:
db.prepare(...).run(...) - 종료 시 닫기:
db.close()
node:sqlite는 "작은 로컬 저장소" 카드에 가깝다
CLI 상태 저장, 데스크톱 앱 캐시, 로컬 메타데이터, 테스트용 DB처럼 외부 DB 서버까지는 필요 없는 경우에 잘 맞습니다.
버전과 안정성 확인이 선행돼야 한다
내장 모듈이라도 모든 런타임에서 바로 같은 조건으로 쓸 수 있는 건 아닙니다. node:sqlite는 v22.5.0에 추가됐고, 초기 22.x에서는 --experimental-sqlite가 필요했습니다. 현재는 v22.13+/v23.4+부터 플래그 없이 쓸 수 있지만, 최신 문서 기준 안정성 단계도 아직 release candidate이므로 현재 배포 런타임 버전과 안정성 단계를 먼저 확인해야 합니다.
API가 동기라는 점이 중요하다
요청 핸들러 한가운데서 오래 도는 동기 DB를 직접 돌릴지 판단하는 카드입니다. 서버 hot path라면 worker 분리도 같이 생각해야 합니다.
prepare()는 성능보다 안전이 먼저다
반복 쿼리 최적화도 있지만, 더 중요한 건 문자열 연결로 SQL을 만들지 않는 습관입니다.
서버 hot path보다 로컬 도구·캐시에 더 잘 맞는다
내장이라 편해 보여도, DatabaseSync는 이벤트 루프를 붙잡습니다.
그래서 HTTP 요청마다 무거운 조회를 때리는 서버 기본값이라기보다, CLI, 테스트, 로컬 캐시, 데스크톱 앱 저장소 쪽이 더 자연스럽습니다.
언제 내장 sqlite가 맞나
체크포인트
- 로컬 파일 DB:
new DatabaseSync("./data.db") - 임시 테스트 DB:
new DatabaseSync(":memory:") - 안전한 쿼리:
prepare()+ 바인딩 - 읽기 성능 보정: WAL 모드 검토
- 이벤트 루프 블로킹 우려: worker 분리
- 도입 전 확인: Node 버전/플래그
- 서버 hot path보다 로컬 저장소/CLI/테스트 용도에 더 잘 맞음
주의할 점
내장이라고 바로 모든 런타임에서 같은 조건으로 되는 것은 아니다. 버전과 플래그 조건을 모르고 도입하면 실행 환경마다 아예 모듈이 없거나 안정성 단계가 달라질 수 있다.
참고 링크
1 sources