PostgreSQL에서 JSON 데이터를 다루는 건 진짜 강력한 기능이지만, 아무리 좋은 도구라도 조심해서 써야 해. 작은 실수 하나가 쿼리를 완전 미궁으로 만들 수도 있거든. 오늘은 PostgreSQL에서 JSON이랑 JSONB를 쓸 때 자주 나오는 실수들과, 그걸 어떻게 피할 수 있는지에 집중해볼게.
문제 1: JSON 대신 JSONB를 안 쓰는 경우
많은 초보자들이 JSON 타입이 JSON 데이터를 저장할 때 더 좋다고 착각해서 그냥 쓰는 경우가 많아. 근데 PostgreSQL에서 JSON은 데이터를 텍스트로 저장해서, 검색이나 필터링할 때 성능이 확 떨어질 수 있어.
실수 예시:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
details JSON
);
INSERT INTO products (details) VALUES ('{"name": "노트북", "price": 1000}');
INSERT INTO products (details) VALUES ('{"name": "노트북", "price": 1000}');
키(price)로 필터링하려고 하면 JSONB에 비해 훨씬 느리게 동작하는 걸 볼 수 있어.
해결 방법: 데이터를 자주 필터링하거나 접근할 계획이면 JSONB를 써!
CREATE TABLE products (
id SERIAL PRIMARY KEY,
details JSONB
);
문제 2: JSONB에 인덱스가 없는 경우
JSONB는 진짜 강력한 도구지만, 인덱스 없이 쓰면 복잡한 쿼리에서 성능이 확 떨어질 수 있어.
실수 예시: 예를 들어, details 컬럼에 JSON 오브젝트를 잔뜩 저장해놓은 테이블이 있다고 해보자:
SELECT * FROM products WHERE details->>'name' = '노트북';
인덱스가 없으면 서버가 모든 레코드를 다 뒤져야 해서(full table scan), 시간이 훨씬 오래 걸려.
해결 방법: 키 검색을 빠르게 하려면 GIN 인덱스를 만들어줘:
CREATE INDEX idx_details_name ON products USING gin (details jsonb_path_ops);
문제 3: 중첩 데이터 추출 실수
중첩된 오브젝트나 배열에서 데이터를 뽑아낼 때 헷갈릴 수 있는데, 특히 ->랑 ->> 연산자의 차이를 모르면 실수하기 쉽지.
실수 예시:
SELECT details->'price' FROM products;
이 쿼리는 값을 JSON 형식으로 반환해. 즉, "1000"이 아니라 1000이 아니라는 거지. 진짜 값이 필요하면 ->>를 써야 해:
SELECT details->>'price' FROM products;
문제 4: 연산자 잘못 쓰는 경우
@> 연산자를 보고 "오, 이거 멋진데? 무조건 써야지!"라고 생각할 수도 있는데, 이게 어떻게 동작하는지 모르면 예상치 못한 결과가 나올 수 있어.
실수 예시:
SELECT * FROM products WHERE details @> '{"price": 1000}';
이 쿼리는 price가 JSON에서 숫자일 때만 동작해. 만약 값이 문자열 "1000"로 저장되어 있으면 결과가 안 나와.
해결 방법: JSON에서 데이터 타입을 꼭 신경 써야 해:
SELECT * FROM products WHERE details->>'price' = '1000';
문제 5: 큰 JSON 오브젝트
최적화 없이 큰 JSON 오브젝트를 저장하면 쿼리 성능이 엄청 느려질 수 있어. 게다가 JSONB 안의 작은 데이터 하나만 바꿔도 전체 오브젝트를 다 읽고 써야 하거든.
해결 방법: 자주 쓰는 키가 있다면, 그건 따로 컬럼으로 빼는 게 좋아. 예를 들어:
ALTER TABLE products ADD COLUMN price NUMERIC;
UPDATE products SET price = (details->>'price')::NUMERIC;
이제 JSONB를 파싱하지 않고도 데이터를 빠르게 필터링하고 정렬할 수 있어.
문제 6: 오브젝트 전체를 다시 만드는 경우
jsonb_set()이나 jsonb_insert() 같은 함수를 쓰면, PostgreSQL이 완전히 새로운 JSONB 오브젝트를 만들어. 이게 성능에 부담이 될 수 있어.
해결 방법: JSONB 업데이트 횟수를 최소화해. 예를 들어, 여러 번 나눠서 업데이트하지 말고 한 번에 처리하는 게 좋아:
UPDATE products
SET details = jsonb_set(details, '{price}', '1500'::jsonb);
문제 7: 배열 구조를 잘못 이해하는 경우
JSONB에서 배열도 신경 써야 해. 예를 들어, 이런 배열이 있다고 해보자:
{
"tags": ["전자제품", "노트북", "세일"]
}
"노트북" 태그가 있는지 확인하고 싶을 때, @> 연산자를 잘못 쓰면 결과가 안 나올 수 있어. 이 연산자는 문자열이 아니라 배열을 기대하거든.
실수 예시:
SELECT * FROM products WHERE details->'tags' @> '"노트북"';
해결 방법: @> 연산자에 올바른 포맷을 써야 해:
SELECT * FROM products WHERE details->'tags' @> '["노트북"]';
실수 방지 꿀팁
JSONB를 쓸 때 이런 팁들을 기억하면 실수 확 줄일 수 있어:
데이터 타입을 제대로 선택하자. 데이터가 많고 자주 필터링한다면 JSON 대신 JSONB를 꼭 써.
인덱스를 꼭 만들자. 특정 키를 자주 조회한다면, GIN 같은 인덱스를 만들어줘.
데이터를 넣기 전에 꼭 체크하자. 데이터 구조를 확인하려면 validation 함수를 써봐:
DO $$
BEGIN
IF jsonb_typeof('{"price": 1000}'::jsonb->'price') IS DISTINCT FROM 'number' THEN
RAISE EXCEPTION '가격은 숫자여야 해';
END IF;
END $$;
데이터 구조를 최적화하자. 자주 쓰는 키는 따로 컬럼으로 빼는 게 좋아.
연산자랑 함수 공부하기. PostgreSQL 공식 문서를 꼼꼼히 읽고 ->, ->>, @>, ?| 등 함수 차이를 잘 알아두자.
JSON이랑 JSONB는 유연하고 복잡한 데이터를 다룰 때 진짜 든든한 친구가 될 수 있어. 중요한 건 도구를 잘 골라서 흔한 실수를 피하고, 네 코드가 효율적이고 관리하기 쉽게 만드는 거야!
GO TO FULL VERSION