두 데이터베이스, 서로 다른 두 세계
SQLite와 PostgreSQL은 둘 다 SQL을 쓰고, 관계형 데이터를 저장하고, 실제 서비스도 충분히 굴릴 수 있습니다. 하지만 이 공통점을 빼고 나면, 둘은 완전히 다른 환경을 위해 설계된 도구입니다.
- SQLite는 라이브러리입니다. 애플리케이션 프로세스 안에서 동작하며, 디스크에 있는 하나의
.db파일을 직접 읽고 씁니다. 별도 서버도, 포트도, 사용자 계정 설정도 필요 없습니다. - PostgreSQL은 서버입니다. 자체 프로세스로 실행되어 네트워크 포트를 리슨하고, 애플리케이션은 클라이언트로서 여기에 접속합니다.
sqlite vs postgresql을 이야기할 때 등장하는 거의 모든 차이점 — 동시성, 배포 방식, 타입 엄격성, 성능 — 은 결국 이 구조적 차이 하나에서 파생됩니다. 글을 읽는 동안 이 점을 머릿속에 두고 따라와 주세요.
아키텍처: 인프로세스 vs 클라이언트/서버
SQLite에서 데이터베이스를 연다는 건, 그냥 파일 하나를 여는 것과 같습니다:
데몬을 띄울 필요도 없고, pg_hba.conf을 수정할 일도, 포트를 열어둘 일도 없습니다. 앱이 SQLite 라이브러리를 로드하고 notes.db 파일을 열면 그때부터 바로 쿼리를 실행할 수 있죠. 배포라고 해봐야 그냥 "파일 복사"가 전부입니다.
반면 Postgres는 이런 모습에 가깝습니다.
# サーバーを起動する(一度だけ、管理者として):
sudo systemctl start postgresql
# その後、アプリから接続する:
psql -h localhost -U alice -d mydb
애플리케이션이 별도의 프로세스(보통은 TCP, 가끔은 Unix 소켓을 통해)와 통신하는 구조입니다. 이 추가 계층 때문에 설정에 시간이 들고 쿼리마다 연결 왕복(round-trip)이 발생하지만, 그 대가로 네트워크 접근성과 다중 사용자 인증, 그리고 진짜 동시 쓰기 능력을 얻게 됩니다.
sqlite postgresql 동시성, 가장 결정적인 차이
sqlite vs postgresql을 고를 때 보통 이 부분이 승부를 가릅니다. SQLite는 쓰기를 직렬화합니다. 즉, 어느 순간이든 단 하나의 writer만 데이터베이스 파일에 락을 걸고, 나머지 writer들은 대기해야 합니다. 읽기는 병렬로 처리할 수 있지만(특히 WAL 모드에서는) 쓰기는 한 번에 하나씩만 가능합니다.
반면 Postgres는 MVCC(다중 버전 동시성 제어)와 row-level 락을 사용합니다. 여러 트랜잭션이 서로 다른 행에 동시에 쓰기를 해도 서로를 막지 않습니다.
실제 사례로 보면:
- 초당 50명이 읽고 작성자 한 명이 가끔 글을 올리는 블로그? SQLite로 충분합니다.
- 수백 명의 사용자가 동시에 재고를 갱신하는 이커머스 결제 시스템? Postgres가 답입니다.
- 모바일 앱의 로컬 캐시? 두말할 것 없이 SQLite입니다.
- 백그라운드 워커가 수십 개씩 돌아가는 멀티 테넌트 SaaS 백엔드? Postgres로 가야 합니다.
WAL 모드 sqlite(PRAGMA journal_mode = WAL;)를 켜면 SQLite의 동시성 특성이 한결 나아집니다. 읽기가 쓰기를 막지 않게 되거든요. 다만 "writer는 한 번에 하나"라는 근본 규칙 자체를 바꿔주지는 않습니다.
타입 시스템: 느슨함 vs 엄격함
Postgres는 엄격합니다. INTEGER로 선언된 컬럼은 문자열을 받지 않습니다. 예외 없습니다:
-- Postgres
CREATE TABLE t (n INTEGER);
INSERT INTO t (n) VALUES ('not a number');
-- 오류: integer 타입에 대한 잘못된 입력 구문
SQLite는 기본적으로 타입 친화성(type affinity) 을 사용합니다. 강제 규칙이 아니라 일종의 권고 사항이라고 보면 됩니다. 그래서 아래와 같은 INSERT도 그대로 통과합니다:
문자열이 INTEGER 컬럼에 그대로 들어가 있습니다. SQLite가 이걸 텍스트로 저장한 거죠. 이런 유연함은 의도적으로 설계된 것인데, 빠른 프로토타이핑에는 편리하지만 오래 유지해야 하는 스키마에서는 오히려 독이 됩니다.
다행히 최신 SQLite(3.37 이상)에서는 PostgreSQL처럼 동작하는 STRICT 테이블을 지원합니다:
새 SQLite 프로젝트를 시작한다면 STRICT를 켜두자. "숫자 컬럼에 왜 문자열이 들어가 있지?" 같은 황당한 상황을 통째로 막아준다.
기능 범위 비교
PostgreSQL은 거의 모든 면에서 더 풍부하다. 데이터 타입만 봐도 배열, 범위, 기하, 네트워크, 사용자 정의 enum이 있고, 절차형 언어(PL/pgSQL, PL/Python), 랭킹 기능까지 갖춘 전문 검색, 머터리얼라이즈드 뷰, 테이블 파티셔닝, 복제, 역할 기반 보안, 그리고 PostGIS·TimescaleDB·pgvector로 이어지는 두터운 확장 생태계까지 모두 제공한다.
SQLite는 핵심만 챙기되 자기 규모에 어울리는 기능들을 곁들인 형태다. JSON 함수, FTS5 기반 전문 검색, R-Tree 인덱스, 윈도우 함수, CTE, 생성 컬럼(generated columns)까지는 지원한다. 빠진 건 전부 "서버"를 전제로 하는 기능들이다. 사용자, 역할, 복제, 네트워크 접속 같은 것 말이다.
대략 이런 식으로 정리하면 편하다:
- GIS, 벡터 검색, 복제가 필요하다? → PostgreSQL.
- iOS 앱 안에 DB를 넣어 배포해야 한다? → SQLite.
- 둘 다 써야 한다? → 많은 팀이 개발과 테스트는 SQLite로, 배포는 PostgreSQL로 가져간다. 다만 이렇게 섞어 쓰면 문법 차이에서 발목을 잡힐 수 있다(아래 참고).
실제로 부딪히게 되는 SQLite vs PostgreSQL 문법 차이
일상적인 SQL은 거의 똑같다. 차이가 몰려 있는 곳은 스키마, 타입, 그리고 몇몇 내장 함수다:
-- 자동 증가 기본 키
-- SQLite:
CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
-- Postgres:
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);
-- 또는 최신 Postgres:
CREATE TABLE users (id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT);
-- 현재 타임스탬프
-- SQLite: CURRENT_TIMESTAMP (텍스트 반환)
-- Postgres: NOW() (타임스탬프 반환)
-- 불리언 타입
-- SQLite: 실제 BOOLEAN 없음; INTEGER 0/1 사용
-- Postgres: TRUE/FALSE를 사용하는 BOOLEAN
SQLite로 개발하고 Postgres로 배포하는 구조라면, ORM이나 마이그레이션 도구를 한 단계 끼워 두는 편이 좋습니다. 그렇지 않으면 두 DB 사이의 미묘한 차이가 애플리케이션 코드까지 그대로 새어 들어옵니다.
sqlite postgresql 성능 비교, 솔직하게
"어느 쪽이 더 빠른가"는 질문을 어떻게 던지느냐에 달려 있습니다. 단일 프로세스가 읽기와 가벼운 쓰기를 처리하는 상황이라면 SQLite를 이기기 어렵습니다. 네트워크 홉도, 프로토콜 파싱도, 클라이언트 커넥션도 없으니까요. 클라이언트가 하나뿐인 단순 쿼리 벤치마크에서는 SQLite가 Postgres를 앞지르는 경우도 흔합니다.
하지만 동시 쓰기 작업자가 많아지거나, 병렬 실행이 필요한 대용량 데이터셋, 혹은 Postgres의 성숙한 플래너 덕을 보는 복잡한 쿼리 플랜이 등장하면 무게추는 Postgres 쪽으로 기웁니다. Postgres는 더 큰 머신과 더 많은 코어를 활용하는 수직 확장에서도 SQLite와는 출발점 자체가 다릅니다.
정직하게 정리하자면, SQLite는 자기가 잘하는 영역에서 빠르고, Postgres도 자기가 잘하는 영역에서 빠릅니다. 벤치마크 헤드라인이 아니라 워크로드의 모양을 보고 고르세요.
sqlite vs postgresql, 빠른 선택 가이드
이럴 땐 SQLite:
- 데이터가 단일 애플리케이션과 함께 사는 경우 — 데스크톱, 모바일, 임베디드, CLI 도구.
- 쓰기가 단일 프로세스 또는 소수의 프로세스에서만 발생하는 경우.
- 별도 설정 없이 곧바로 배포하고 싶은 경우.
- 인프라보다는 스키마 설계에 집중하고 싶은 프로토타이핑 단계.
이럴 땐 Postgres:
- 여러 애플리케이션 서버나 워커가 동시에 DB에 쓰기를 수행하는 경우.
- 다수의 클라이언트가 네트워크로 접근해야 하는 경우.
- 롤(role), 복제, GIS, 사용자 정의 타입, 저장 프로시저 같은 고급 기능이 필요한 경우.
- 운영 서비스의 핵심 데이터를 영구적으로 보관해야 하는 경우.
자주 보이는 경로는 이렇습니다. 작은 프로젝트는 SQLite로 시작하고, 트래픽 양상이 정말 그것을 요구할 때 Postgres로 갈아타는 거죠. sqlite to postgresql 마이그레이션이 공짜는 아니지만 이미 정형화된 작업이고, 사실 대부분의 프로젝트는 그 단계까지 가지도 않습니다.
다음 글: SQLite가 정답일 때
여기까지가 양쪽의 트레이드오프입니다. 다음 페이지에서는 SQLite의 긍정적인 면을 더 깊이 들여다봅니다. 단순히 그럭저럭 쓸 만한 수준이 아니라 오히려 더 나은 선택지가 되는 워크로드는 어떤 것인지, 그리고 "이제 SQLite로는 한계다"라는 신호가 무엇인지 짚어 봅니다.
자주 묻는 질문
SQLite와 PostgreSQL의 가장 큰 차이는 뭔가요?
SQLite는 앱 프로세스 안에 내장돼서 단일 파일을 직접 읽고 쓰는 라이브러리입니다. 반면 PostgreSQL은 별도의 서버로 띄워두고 네트워크로 접속해서 쓰는 구조죠. 이 아키텍처 차이 하나가 동시성, 배포 방식, 타입 시스템, 툴링까지 거의 모든 비교 포인트를 결정짓습니다.
SQLite가 PostgreSQL보다 빠른가요?
단일 프로세스에서 읽기와 가벼운 쓰기를 할 때는 SQLite가 더 빠른 경우가 많습니다. 네트워크 왕복도 없고 클라이언트/서버 프로토콜 오버헤드도 없으니까요. 하지만 여러 클라이언트가 동시에 쓰기를 하면 row-level locking과 MVCC를 지원하는 PostgreSQL이 앞섭니다. 결국 '빠르다'는 건 엔진보다 워크로드에 달린 문제입니다.
SQLite를 운영 환경에서 써도 되나요?
워크로드 성격만 맞으면 충분히 가능합니다. 실제로 웹사이트, 데스크톱 앱, 임베디드 기기에서 SQLite가 멀쩡히 운영되고 있어요. 한계는 동시 쓰기입니다. 여러 프로세스가 동시에 쓰기를 해야 한다면 PostgreSQL은 자연스럽게 처리하지만, SQLite는 쓰기를 직렬화합니다. WAL 모드가 어느 정도 완화해 주긴 해도 한계 자체가 사라지진 않습니다.
SQLite에서 PostgreSQL로 어떻게 마이그레이션하나요?
sqlite3 mydb.db .dump로 스키마와 데이터를 덤프한 다음, SQL을 손봐야 합니다. AUTOINCREMENT는 SERIAL이나 GENERATED AS IDENTITY로 바꾸고, 타입 이름도 갈아끼워야 하고, SQLite 특유의 느슨한 타입(loose typing)에 의존하던 부분은 정리가 필요합니다. pgloader 같은 도구가 대부분을 자동화해 주긴 하지만, 유연한 타입에 의존하던 코드는 다시 짠다고 생각하는 게 마음 편합니다.