JSONB (JSON 타입·쿼리·GIN 인덱스)¶
개요¶
PostgreSQL은 JSON 데이터를 저장할 수 있는 타입을 두 가지 제공해요 — json과 jsonb 입니다. 사실 text로도 JSON을 저장할 수 있지만, 전용 타입을 쓰면 저장된 값이 실제로 JSON 규칙에 맞는지 강제할 수 있고, JSON 전용 함수와 연산자를 쓸 수 있어요.
이 페이지는 그 두 타입이 어떻게 다른지, 문서형 데이터를 PostgreSQL 안에서 어떻게 저장·조회·인덱싱하는지를 공식 문서 기준으로 풀어요. "관계형 DB인데 JSON도 다루고 싶다" 할 때 읽으면 돼요.
핵심 개념¶
json과 jsonb — 뭐가 다른가요?¶
둘 다 입력으로 거의 같은 JSON을 받지만, 저장 방식이 달라서 성능과 표현이 갈려요.
json— 입력 텍스트를 깨끗한 상태 그대로(정확한 복사본) 저장해요. 공백, 키 순서, 중복 키까지 그대로 보존해요. 대신 처리할 때마다 매번 다시 파싱해야 해서 느려요.jsonb— 입력을 분해된 이진(binary) 형식으로 저장해요. 저장할 때 변환 비용이 조금 들지만, 처리는 훨씬 빨라요(재파싱 불필요). 그리고 인덱스를 지원한다는 게 결정적 장점이에요. 대신 공백은 보존하지 않고, 키 순서를 보장하지 않으며, 중복 키는 마지막 값만 남겨요.
공식 문서는 특별한 필요(예: 키 순서를 보존해야 하는 레거시 요구)가 없으면 대부분 jsonb를 쓰라고 권장해요.
JSONB 연산자 — 문서를 어떻게 다루나요¶
->— 키로 값 꺼내기(JSONB 반환),->>— 키로 값 꺼내기(텍스트 반환)@>(포함, containment) — 한 문서가 다른 문서를 안에 포함하는지 검사해요. 객체는 부분 키-값 쌍만 일치해도 포함으로 보고, 배열은 요소 순서를 무시해요. 예)doc @> '{"company": "Magnafone"}'—company가 그 값인 문서 찾기.?(존재, existence) — 어떤 문자열이 최상위 키나 배열 요소로 존재하는지 검사해요. (중첩된 곳은 보지 않아요.)?|,?&— 문자열 중 하나라도(OR), 전부(AND) 존재하는지 검사해요.
포함(@>) 연산은 중첩된 구조를 통째로 검사할 수 있어서, 예를 들어 tags 배열 안에 특정 항목들이 모두 있는 문서를 한 번에 찾는 데 강해요.
jsonb 인덱싱 — GIN¶
jsonb는 GIN 인덱스로 효율적으로 검색할 수 있어요. GIN은 문서 안에 등장하는 키·키-값 쌍을 역색인으로 만들어, 많은 문서 중에서 원하는 항목을 빠르게 찾게 해줘요.
- 기본 GIN 연산자 클래스 —
?,?|,?&(존재),@>(포함),@?/@@(jsonpath 매칭)을 지원해요.CREATE INDEX idxgin ON api USING GIN (jdoc); jsonb_path_ops— 존재 연산자(?)는 지원하지 않지만@>는 지원하고, 보통 인덱스가 더 작고 빠른 경우가 많아요.CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);
인덱스를 만들어도 항상 쓰이는 건 아니에요. 연산자가 인덱스가 걸린 컬럼에 직접 적용돼야 인덱스를 타요. 예를 들어 jdoc->'tags' ? 'qui'처럼 ->를 거친 뒤 ?를 쓰면 기본 인덱스는 활용 못 해요. 그런 쿼리가 잦다면 표현식 인덱스 CREATE INDEX idxgintags ON api USING GIN ((jdoc->'tags')); 같은 걸 만들 수 있어요.
사용 사례 / 실제 적용¶
- 구조가 자유로운 문서 저장 — 노트·캔버스처럼 형태가 수시로 바뀌는 데이터를
jsonb로 저장해요. 마이그레이션 없이 새 필드를 담을 수 있어요. - 포함 검색으로 문서 걸러내기 —
doc @> '{"tags": [...]}'처럼 중첩 구조를 기준으로 원하는 문서만 뽑아요. 키-값 쌍 부분 일치로 간결하게 조건을 써요. - existence로 키 존재 확인 — "어떤 키를 가진 문서" 같은 걸 빠르게 찾아요.
- GIN 인덱스로 검색 가속 — 문서가 많아지면
jsonb컬럼에 GIN 인덱스를 걸어@>·?쿼리를 빠르게 해요. 중첩 항목 검색이 잦으면 표현식 GIN 인덱스(jdoc->'tags')까지 고려해요.
관계형 안에서 문서형 데이터를 다룰 수 있게 해주는 게 JSONB의 가치예요. 다만 큰 문서를 자주 갱신하면 행 전체에 잠금이 걸리고 인덱스 부담이 커지니, 문서는 적절한 크기로 쪼개 두는 게 좋아요.
더 알아보기¶
- 공식 문서 (1차)
- JSON 타입 (JSON Types) — postgresql.org/docs/current/datatype-json.html
- JSON 함수와 연산자 — postgresql.org/docs/current/functions-json.html
- 큐레이션/블로그 (2차)
- JSONB 모범 사례 모음 — nabeta/postgresql-jsonb