SQLite에서는 STRICT 테이블을 우선 사용하라

2 hours ago 1
  • 테이블 정의 끝에 STRICT 를 추가하면 정수 열에 임의의 텍스트가 들어가는 타입 오류를 조기에 차단해 데이터 무결성을 높일 수 있음
  • 삽입·갱신 시 타입을 검사하면서도 '123'처럼 손실 없이 변환 가능한 값은 허용하며, 열 타입은 INT, INTEGER, REAL, TEXT, BLOB, ANY 로 제한됨
  • 여러 타입을 담아야 하는 열에는 ANY 를 지정할 수 있어 엄격한 검증과 유연한 저장을 한 테이블 안에서 함께 적용 가능함
  • 기존 테이블을 곧바로 STRICT로 바꿀 수는 없어 새 테이블 생성과 데이터 복사가 필요하며, 잘못된 기존 데이터는 정리하거나 형 변환해야 함
  • STRICT 테이블은 SQLite 3.37.0 이상에서만 지원되며 이론적인 검사 비용은 있지만, 비공식 실험에서는 뚜렷한 성능·파일 크기 차이가 확인되지 않았음

STRICT 테이블 생성

  • SQLite의 STRICT 테이블은 다른 SQL 엔진과 비슷하게 더 엄격한 타입 검사를 적용함
  • CREATE TABLE 정의 마지막에 STRICT 를 붙이면 됨
CREATE TABLE people (name TEXT) STRICT;

삽입과 갱신의 타입 검사

  • 일반 SQLite 테이블은 INTEGER 열에도 'garbage' 같은 텍스트를 저장할 수 있지만, STRICT 테이블은 타입 불일치를 오류로 처리함
CREATE TABLE people_nonstrict (age INTEGER); INSERT INTO people_nonstrict (age) VALUES ('garbage'); -- 정상 처리됨 CREATE TABLE people_strict (age INTEGER) STRICT; INSERT INTO people_strict (age) VALUES ('garbage'); -- 오류: cannot store TEXT value in INTEGER column
  • 같은 검증이 UPDATE 에도 적용돼 저장 후 값을 변경할 때도 잘못된 타입을 막음
  • 단, 값이 손실 없이 변환될 수 있다면 STRICT 테이블에서도 허용됨
    • 문자열 '123'은 정수 123으로 완전히 변환할 수 있어 아래 두 삽입이 동일하게 처리됨
INSERT INTO people_strict (age) VALUES ('123'); INSERT INTO people_strict (age) VALUES (123);

테이블 정의 단계의 타입 제한

  • 일반 테이블에서는 SQLite가 지원하지 않는 타입 이름도 열 선언에 사용할 수 있음
  • STRICT 테이블은 지원하지 않는 타입 이름을 사용하면 생성 단계에서 오류가 발생함
CREATE TABLE tbl (name GARBAGE) STRICT; CREATE TABLE tbl (name DATETIME) STRICT; CREATE TABLE tbl (name JSON) STRICT; CREATE TABLE tbl (name UUID) STRICT; CREATE TABLE tbl (name BLOBB) STRICT;
  • 허용되는 열 타입은 INT, INTEGER, REAL, TEXT, BLOB, ANY 뿐임
  • 모든 열에 타입을 지정해야 하므로 CREATE TABLE tbl (name)처럼 타입을 생략할 수 없음

ANY로 유연성 유지

  • 타입이 일정하지 않은 데이터를 저장해야 한다면 ANY 열을 사용할 수 있음
  • STRICT 테이블 안에서도 ANY 열에는 정수, 텍스트, 실수, BLOB 등 모든 타입을 저장 가능함
CREATE TABLE tbl (value ANY) STRICT; INSERT INTO tbl (value) VALUES (123); INSERT INTO tbl (value) VALUES ('text'); INSERT INTO tbl (value) VALUES (12.34); INSERT INTO tbl (value) VALUES (X'8647');

기존 테이블 마이그레이션

  • 기존 비엄격 테이블을 ALTER로 곧바로 STRICT 테이블로 바꾸는 방법은 없으므로 처음부터 엄격하게 생성하는 편이 간단함
  • 기존 테이블을 전환하려면 새 STRICT 테이블 생성 → 데이터 복사 → 기존 테이블 교체 절차를 거쳐야 함
CREATE TABLE new_people (name TEXT) STRICT; INSERT INTO new_people SELECT * FROM people; DROP TABLE people; ALTER TABLE new_people RENAME TO people;
  • 기존 데이터의 정수 열에 텍스트가 들어 있는 등 타입이 잘못됐다면 복사 과정에서 오류가 날 수 있음
    • 마이그레이션 전에 데이터를 정리하거나 CAST로 형 변환해야 할 수 있음
  • 새 테이블에만 STRICT를 적용하는 정책도 가능하지만, 테이블마다 검증 강도가 달라져 모든 테이블이 느슨한 경우보다 동작을 예측하기 어려울 수 있음

유연한 타입이 적합한 경우

  • SQLite 개발진은 유연한 타입의 장점을 별도로 정리하며 기본 동작이 유용한 사례를 제시함
  • 비엄격 테이블이 합리적일 수 있는 용도는 다음과 같음
    • 여러 타입을 그대로 담는 순수 키-값 저장소
    • 타입이 서로 다른 기타 속성을 보관하는 공간
    • 지저분한 CSV를 직접 가져오면서 유효하지 않은 값까지 잃지 않고 보존해야 하는 경우
  • 그러나 예상하지 못한 타입은 미묘한 버그를 일으킬 수 있으므로, 일반적인 테이블에서는 조용히 허용하기보다 즉시 오류를 내는 편이 유리함
  • SQLite 소스에는 비엄격 테이블을 legacy라고 부르는 주석도 있지만, 공식 문서보다 신뢰할 근거로 삼기는 어려움

버전 호환성

  • STRICT 테이블은 2021년 11월 출시된 SQLite 3.37.0 에서 도입됨
  • 이전 SQLite 버전에서는 STRICT 테이블을 사용할 수 없음
  • 구버전은 STRICT 테이블이 포함된 데이터베이스도 읽지 못함
    • 최신 SQLite에서 STRICT 테이블을 생성한 뒤 SQLite 3.36.0으로 같은 데이터베이스를 열면 오류가 발생함

성능과 저장 공간

  • STRICT 테이블은 삽입·갱신 시 데이터 타입을 추가로 검사하므로 이론적으로는 더 느릴 수 있음
  • 100개 열을 가진 테이블에 수백만 행을 삽입한 비공식 실험에서는 여러 컴퓨터 모두 뚜렷한 성능 차이가 나타나지 않았음
  • 디스크의 데이터베이스 파일 크기도 같았지만, 철저한 벤치마크가 아니어서 발견하지 못한 차이가 있을 수 있음
  • 열 선호도(column affinity)에 맞지 않는 값이 실수로 저장되는 일을 막아 오히려 성능이 나아질 가능성도 있으나, 별도로 검증되지는 않았음

적용 시 판단할 점

  • STRICT 테이블이 모든 데이터 문제를 해결하지는 않지만, 타입 관련 실수를 줄이고 데이터 무결성을 강화함
  • 대부분은 테이블 정의에 STRICT만 추가하면 돼 적용이 간단함
  • 일반적인 테이블에서는 엄격한 타입 검증으로 얻는 이점이 마이그레이션 부담, 구버전 비호환성, 제한된 유연성보다 큼
Read Entire Article