콘텐츠로 이동

JSONB (JSON 타입·쿼리·GIN 인덱스)

개요

PostgreSQL은 JSON 데이터를 저장할 수 있는 타입을 두 가지 제공해요 — jsonjsonb 입니다. 사실 text로도 JSON을 저장할 수 있지만, 전용 타입을 쓰면 저장된 값이 실제로 JSON 규칙에 맞는지 강제할 수 있고, JSON 전용 함수와 연산자를 쓸 수 있어요.

이 페이지는 그 두 타입이 어떻게 다른지, 문서형 데이터를 PostgreSQL 안에서 어떻게 저장·조회·인덱싱하는지를 공식 문서 기준으로 풀어요. "관계형 DB인데 JSON도 다루고 싶다" 할 때 읽으면 돼요.

핵심 개념

jsonjsonb — 뭐가 다른가요?

둘 다 입력으로 거의 같은 JSON을 받지만, 저장 방식이 달라서 성능과 표현이 갈려요.

  • json — 입력 텍스트를 깨끗한 상태 그대로(정확한 복사본) 저장해요. 공백, 키 순서, 중복 키까지 그대로 보존해요. 대신 처리할 때마다 매번 다시 파싱해야 해서 느려요.
  • jsonb — 입력을 분해된 이진(binary) 형식으로 저장해요. 저장할 때 변환 비용이 조금 들지만, 처리는 훨씬 빨라요(재파싱 불필요). 그리고 인덱스를 지원한다는 게 결정적 장점이에요. 대신 공백은 보존하지 않고, 키 순서를 보장하지 않으며, 중복 키는 마지막 값만 남겨요.

공식 문서는 특별한 필요(예: 키 순서를 보존해야 하는 레거시 요구)가 없으면 대부분 jsonb를 쓰라고 권장해요.

JSONB 연산자 — 문서를 어떻게 다루나요

  • -> — 키로 값 꺼내기(JSONB 반환), ->> — 키로 값 꺼내기(텍스트 반환)
  • @> (포함, containment) — 한 문서가 다른 문서를 안에 포함하는지 검사해요. 객체는 부분 키-값 쌍만 일치해도 포함으로 보고, 배열은 요소 순서를 무시해요. 예) doc @> '{"company": "Magnafone"}'company가 그 값인 문서 찾기.
  • ? (존재, existence) — 어떤 문자열이 최상위 키나 배열 요소로 존재하는지 검사해요. (중첩된 곳은 보지 않아요.)
  • ?|, ?& — 문자열 중 하나라도(OR), 전부(AND) 존재하는지 검사해요.

포함(@>) 연산은 중첩된 구조를 통째로 검사할 수 있어서, 예를 들어 tags 배열 안에 특정 항목들이 모두 있는 문서를 한 번에 찾는 데 강해요.

jsonb 인덱싱 — GIN

jsonbGIN 인덱스로 효율적으로 검색할 수 있어요. 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의 가치예요. 다만 큰 문서를 자주 갱신하면 행 전체에 잠금이 걸리고 인덱스 부담이 커지니, 문서는 적절한 크기로 쪼개 두는 게 좋아요.

더 알아보기