에듀테크 프로젝트 대시보드
JSON / JSONB
반정형 데이터를 컬럼 하나에 담습니다. 저장·조회는 거의 항상 jsonb 를 씁니다.
json | jsonb | |
|---|---|---|
| 저장 | 입력 텍스트 그대로 | 파싱된 바이너리 |
| 공백·키 순서·중복키 | 보존 | 정규화(중복키는 마지막 값) |
| 연산자 인덱싱 | 불가 | GIN 가능 |
| 쓰기 속도 | 약간 빠름 | 약간 느림 (파싱 비용) |
특별한 이유가 없으면 jsonb.
-- data = {"user": {"name": "김한양"}, "tags": ["a", "b"], "age": 17}
SELECT
data -> 'user' AS as_json, -- {"name": "김한양"} (jsonb)
data -> 'user' ->> 'name' AS as_text, -- 김한양 (text)
data #>> '{user,name}' AS by_path, -- 김한양
data -> 'tags' ->> 0 AS first_tag, -- a
(data ->> 'age')::int AS age
FROM profiles;-- 포함(containment) — 가장 자주 쓰는 검색
SELECT * FROM profiles WHERE data @> '{"age": 17}';
SELECT * FROM profiles WHERE data -> 'tags' ? 'a'; -- 키/요소 존재
-- GIN 인덱스
CREATE INDEX idx_profiles_data ON profiles USING gin (data);
-- @> 만 쓸 거면 더 작고 빠른:
CREATE INDEX idx_profiles_data ON profiles USING gin (data jsonb_path_ops);UPDATE profiles SET data = data || '{"verified": true}' WHERE id = 1; -- 병합
UPDATE profiles SET data = jsonb_set(data, '{user,name}', '"이한양"') WHERE id = 1;
UPDATE profiles SET data = data - 'age' WHERE id = 1; -- 키 삭제
UPDATE profiles SET data = data #- '{user,name}' WHERE id = 1; -- 경로 삭제jsonb 안에 두지 말고 정식 컬럼으로 빼세요age int GENERATED ALWAYS AS ((data->>'age')::int) STORED