TECH 으로 돌아가기
TECH HACKER NEWS 오늘 7분 읽기 33 READS

SQLite를 문서형 DB처럼 쓰기: 생성 컬럼이라는 무기

관계형 데이터베이스에 익숙한 개발자라면 스키마를 먼저 정의하고 데이터를 그 틀에 맞춰 넣는 방식을 당연하게 여긴다. 반면 MongoDB나 Elasticsearch 같은 문서형 데이터베이스는 구조가 유동적인 JSON을 그대로 저장하고 필요한 필드만 나중에 뽑아 색인하는 유연함을 무기로 삼는다. PostgreSQL도 JSONB 타입과 표현식 인덱스를 통해 오래전부터 이런 접근을 지원해 왔다. 흥미로운 점은, 서버가 필요 없는 임베디드 데이터베이스인 SQLite에서도 비슷한 패턴을 구현할 수 있게 됐다는 사실이다.

SQLite는 예전부터 JSON 함수들을 제공해 왔지만, 결정적인 조각은 3.31.0 버전(2020년 1월 22일 릴리스)에서 추가된 '생성 컬럼(generated columns)'이었다. 생성 컬럼은 다른 컬럼의 값을 기반으로 자동 계산되는 컬럼이다. 여기에 JSON에서 특정 경로의 값을 꺼내는 json_extract 함수를 결합하면, JSON 원본을 통째로 넣어두고 그 안의 필드를 별도 컬럼처럼 노출시켜 색인까지 걸 수 있다. 즉 하나의 JSON 컬럼만 있는 단출한 테이블로 시작해, 데이터를 다루다가 쓸모 있는 필드를 발견할 때마다 컬럼과 인덱스를 덧붙여 나가는 점진적 스키마 설계가 가능해진다.

검증과 제약을 공짜로 얻는다

SQLite에는 엄밀한 의미의 JSON 타입이 없다. 그래서 텍스트 컬럼에 아무 문자열이나 밀어 넣어도 데이터베이스는 그것이 유효한 JSON인지 따지지 않는다. 통상적으로는 삽입 시 json() 함수를 거쳐 최소화·검증을 하도록 권장하지만, 강제되는 규칙이 아니라 개발자가 깜빡하면 그만이다. 그런데 GENERATED ALWAYS 절에 json_extract를 걸어두면 상황이 달라진다. 잘못된 JSON을 넣는 순간 값 추출에 실패하면서 INSERT 시점에 'malformed JSON' 오류가 발생한다. 검증 로직을 따로 붙이지 않아도, 컬럼을 파생시킨다는 행위 자체가 형식 검사 역할을 해주는 셈이다.

여기서 한 걸음 더 나아가 데이터의 완전성도 강제할 수 있다. 추출한 생성 컬럼에 NOT NULL 제약을 걸면 해당 필드가 빠진 JSON은 거부된다. 물론 SQLite가 제공하는 다른 제약(CHECK 등)이나 기능과도 조합할 수 있다. 스키마리스의 유연함과 관계형의 무결성 검증을 한 테이블 안에서 취향껏 섞어 쓸 수 있다는 뜻이다.

VIRTUAL과 STORED, 그리고 인덱스

생성 컬럼에는 두 가지 저장 방식이 있다. VIRTUAL은 값을 실제로 저장하지 않고 조회 시점에 계산하며, STORED는 계산 결과를 물리적으로 캐싱해 둔다. STORED는 읽기 성능에 유리하지만, 이미 만들어진 테이블에 ALTER TABLE로 나중에 추가할 수 없다는 제약이 있다. 원문 예시들이 VIRTUAL을 택한 이유이기도 하다.

중요한 것은, VIRTUAL로 정의한 컬럼이라도 인덱스를 걸 수 있다는 점이다. 값이 저장돼 있지 않아도 색인은 별도로 구축되므로, 저장 공간을 아끼면서 조회 성능은 확보하는 절충이 가능하다. 실제로 ALTER TABLE로 새 컬럼을 추가하고 그 위에 인덱스를 얹는 흐름이 매끄럽게 이어진다. 데이터를 먼저 쌓아두고, 쿼리 패턴이 드러난 뒤에 그에 맞춰 색인을 설계하는 후행적 최적화가 자연스럽게 성립하는 것이다.

실무에서의 쓸모와 한계

이 패턴이 특히 빛을 발하는 대표적 사례가 웹훅 수신이다. 외부 서비스가 보내는 페이로드는 구조가 자주 바뀌고 제공자마다 제각각이라, 미리 정규화된 스키마로 받으려다 보면 필드가 하나 추가될 때마다 마이그레이션에 시달린다. 대신 들어온 데이터를 JSON 그대로 한 컬럼에 적재해 두면 일단 유실 없이 원본을 보존할 수 있고, 나중에 분석이나 처리에 필요한 필드만 생성 컬럼으로 꺼내 쓰면 된다. 감사 로그나 이벤트 수집처럼 '일단 다 저장하고 나중에 판단'하는 워크로드에도 잘 맞는다.

다만 현실적인 장벽도 있다. 이 기능을 쓰려면 충분히 최신 버전의 SQLite가 필요한데, 원문 작성 시점 기준으로는 오히려 이 새 버전을 구하는 것이 가장 까다로운 부분이었다. macOS의 Homebrew에는 포함돼 있었지만, 환경에 따라서는 nixpkgs-unstable 같은 불안정 채널에 의존해야 하는 경우도 있었다. 지금은 상황이 많이 나아졌겠지만, 시스템에 기본 탑재된 SQLite 버전을 그대로 신뢰하기보다 실제 버전을 확인하는 습관이 필요하다.

결국 이 접근은 SQLite를 무거운 문서형 DB의 완전한 대체재로 격상시키는 것이 아니라, 별도 서버 없이 파일 하나로 돌아가는 임베디드 환경에서 문서형 저장의 편의를 상당 부분 흉내 낼 수 있게 해준다는 데 의미가 있다. 대규모 분산 검색이 필요하다면 여전히 전용 엔진이 답이겠지만, 경량 애플리케이션이나 프로토타입, 로컬 도구에서는 의존성 하나 늘리지 않고도 유연한 데이터 모델을 손에 넣는 실용적인 선택지가 된다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://dgl.cx/2020/06/sqlite-json-support
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...