이 문서는 자라는 중인 질문입니다. 아직 증거를 모으고 있으며, 내용이 바뀔 수 있습니다.
한 줄로 말하면
SQLite STRICT 테이블은 SQLite의 느슨한 타입 규칙을 테이블 단위로 좁혀, 값이 들어오는 순간에 타입 오류를 더 빨리 드러내는 장치야. SQLite를 완전히 엄격한 데이터베이스로 바꾸는 기능은 아니고, 느슨함이 필요한 칸과 실수로 느슨해진 칸을 구분하게 해준다.
비유로 이해하기
일반 SQLite 테이블은 물건을 넣는 서랍에 “숫자”라고 라벨을 붙여도, 누가 종이를 넣으면 일단 받아줄 수 있는 서랍에 가까워. 나중에 그 서랍을 숫자 계산에 쓰려는 순간에야 문제가 보일 수 있어.
STRICT 테이블은 서랍마다 입구 검사를 하나 더 두는 방식이야. “정수” 칸에는 정수로 손실 없이 저장할 수 있는 값만 들어가고, 그렇지 않으면 넣는 순간 오류가 난다.1 여기까지가 쉽게 이해를 돕는 비유야. 실제로는 입력의 글자 모양만 보는 게 아니라, SQLite가 선언한 저장 타입으로 안전하게 바꿀 수 있는지를 본다.
정확한 정의
STRICT 테이블은 CREATE TABLE 문 끝에 STRICT를 붙여 만든다. 이렇게 만든 테이블에서는 열 선언에 쓸 수 있는 타입 이름이 INT, INTEGER, REAL, TEXT, BLOB, ANY로 제한된다.1 일반 테이블처럼 DATETIME, JSON, 오타가 섞인 임의 타입 이름을 그냥 받아들이지 않는다.
데이터를 넣거나 고칠 때도 차이가 생긴다. 값이 열의 선언 타입으로 손실 없이 저장될 수 있으면 들어가고, 그렇지 않으면 오류가 난다. 예를 들어 정수 칸에 정수로 바뀔 수 없는 텍스트를 넣으면 조용히 저장하지 않는다. 이건 애플리케이션 코드가 나중에 이상한 값을 만나기 전에, 저장 단계에서 실패하게 만드는 장치야.
또 하나 중요한 타입이 ANY야. ANY 열은 여러 형태의 값을 그대로 담을 수 있다.1 그래서 로그의 부가 속성, 외부 JSON에서 온 원본 값, 플러그인별 확장 필드처럼 모양이 일정하지 않은 값은 ANY로 열어 둘 수 있어. 대신 핵심 식별자, 금액, 수량, 상태값처럼 애플리케이션 로직이 강하게 기대하는 칸은 좁은 타입으로 잠그는 식이 된다.
왜 중요한가
SQLite의 강점은 파일 하나로 가볍게 돌아가고, 오래된 애플리케이션과 잘 맞는다는 점이야. 그 유연함 덕분에 작은 앱, 모바일 앱, 로컬 캐시, 테스트 데이터베이스에서 쓰기 쉽다. 하지만 같은 유연함이 애플리케이션 테이블에서는 버그를 늦게 발견하게 만들 수 있어.
STRICT 테이블의 핵심 가치는 빨라진 실패야. 데이터가 들어오는 순간에 틀린 값을 거절하면, 나중에 화면·분석·동기화 코드에서 이상한 동작으로 번지는 일을 줄일 수 있다. feedback loop 관점으로 보면, 저장 시점의 오류가 개발자에게 더 짧은 확인 고리를 주는 셈이야.
다만 이 기능은 “항상 켜면 좋은 스위치”로 끝나지 않는다. SQLite 3.37.0에서 추가된 기능이라, 더 오래된 SQLite 라이브러리는 STRICT 테이블이 들어간 데이터베이스 파일을 열지 못할 수 있다.2 앱이 여러 운영체제와 내장 SQLite 버전에 걸쳐 배포된다면, 타입 안정성보다 먼저 실행 환경의 버전 경계를 확인해야 해.
실제 예시
새 테이블을 만든다면 형태는 단순해.
CREATE TABLE people (
id INTEGER PRIMARY KEY,
age INTEGER,
name TEXT,
metadata ANY
) STRICT;이 테이블에서 age는 정수로 저장할 수 있는 값이어야 한다. name은 텍스트로 저장되고, metadata는 여러 형태의 값을 담을 수 있는 예외 칸이 된다. 그러니까 모든 유연함을 없애는 게 아니라, 유연해야 하는 자리를 명시하는 구조야.
기존 테이블을 바꾸는 일은 더 조심스럽다. 비엄격 테이블을 ALTER TABLE 한 줄로 STRICT 테이블로 바꾸는 길은 없다. 새 STRICT 테이블을 만들고, 기존 데이터를 복사하고, 이름을 교체하는 식의 이전이 필요하다.3 이때 이미 들어 있던 값이 선언 타입과 어긋나 있으면 복사 중에 드러날 수 있다.
헷갈리지 말아야 할 점
- STRICT는 SQLite를 PostgreSQL로 바꾸지 않아. SQLite의 파일 기반 배포, 동적 타입 역사, 하위 호환성은 그대로 남아 있어.
- 문자열 입력을 무조건 막는 기능이 아니야. 선언 타입으로 손실 없이 저장할 수 있으면 허용될 수 있다. 중요한 기준은 겉모양보다 저장 타입과 손실 여부야.
- ANY는 실패가 아니라 의식적인 예외야. 정말 여러 형태의 값을 받아야 하는 칸은
ANY로 열어 두고, 나머지 칸을 엄격하게 잠그는 게 핵심이다. - 기존 데이터베이스에서는 데이터 청소가 먼저야. STRICT 자체보다 이미 들어간 값이 선언 타입과 얼마나 어긋나 있는지가 이전 비용을 결정한다.
관련 문서
- SQLite STRICT 테이블을 읽은 흐름 글: SQLite는 숫자 칸에 글자를 넣어도 조용히 넘어간다
- 오류를 빨리 읽고 행동을 고치는 구조: feedback loop
남은 질문들
- 기존 SQLite 데이터베이스를 STRICT 테이블로 옮길 때 가장 자주 드러나는 타입 불일치는 무엇일까?
- ORM과 마이그레이션 도구는
STRICT와ANY를 기본 스키마 표현 안에 얼마나 자연스럽게 넣을 수 있을까? - 저장 시점 타입 검사가 실제 쓰기 성능이나
PRAGMA integrity_check운영 시간에 의미 있는 비용을 만들까?
댓글