이제 MySQL에서 JSON을 실제로 사용하는 방법을 알아보자. MySQL 5.7부터 JSON 타입을 지원하며, 8.0에서 많은 기능이 추가되었다.
JSON 컬럼이 있는 테이블 생성
-- 기존 테이블 삭제
DROP TABLE IF EXISTS product_json;
-- JSON 컬럼이 있는 상품 테이블 생성
CREATE TABLE product_json (
product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
category VARCHAR(100) NOT NULL,
attributes JSON,
created_at DATETIME NOT NULL DEFAULT NOW()
);
- attributes 컬럼의 타입이 JSON인 것을 확인할 수 있다.
- MySQL은 이 컬럼에 유효한 JSON만 저장되도록 자동으로 검증한다.
JSON 데이터 삽입
문자열로 직접 삽입
INSERT INTO product_json (name, category, attributes) VALUES
('갤럭시 S24','스마트폰','{"screen_size": 6.2, "storage": 256, "color": "팬텀 블랙", "ram": 8, "5g":true}');
- JSON 데이터를 문자열로 직접 삽입하는 경우 JSON 형식을 자유롭게 입력할 수 있다.
JSON_OBJECT 함수 사용
INSERT INTO product_json (name, category, attributes) VALUES
('아이폰 15 프로','스마트폰',
JSON_OBJECT(
'screen_size', 6.1,
'storage', 256,
'color', '블랙 티타늄',
'ram', 8,
'5g', true
));
- "JSON_OBJECT" 함수를 사용하면 키-값 쌍을 직접 지정할 수 있다.
JSON_ARRAY 함수 사용
INSERT INTO product_json (name, category, attributes) VALUES
('맥북 프로 16', '노트북',
JSON_OBJECT(
'screen_size', 16.2,
'storage', 512,
'color', '스페이스 그레이',
'ram', 18,
'ports', JSON_ARRAY(
'HDMI',
'USB-C',
'MagSafe',
'SD카드'
)));
- 배열을 만들 때는 "JSON_ARRAY" 함수를 사용한다.
데이터 확인
SELECT product_id, name, category, attributes FROM product_json;

추가 샘플 데이터 입력
INSERT INTO product_json (name, category, attributes) VALUES
('갤럭시 탭 S9', '태블릿', '{"screen_size": 11.0, "storage": 128, "color": "그라파이트", "ram": 8, "5g":false, "s_pen": true}'),
('소니 WH-1000XM5','헤드폰','{"color": "블랙", "noise_canceling": true, "battery_life": 30, "driver_size":30, "bluetooth": "5.2"}'),
('LG 그램 17','노트북','{"screen_size": 17.0, "storage": 512, "color": "화이트", "ram": 16, "weight":1.35, "ports": ["USB-C", "USB-A", "HDMI"]}'),
('에어팟 프로 2','이어폰','{"color": "화이트", "noise_canceling": true, "battery_life": 6,"case_battery": 30, "spatial_audio": true}'),
('갤럭시 워치 6','스마트워치','{"screen_size": 1.5, "color": "실버", "battery_life": 40, "water_resistant":true, "gps": true}');
JSON 데이터 조회
전체 JSON 조회
SELECT name, attributes FROM product_json WHERE product_id = 1;

JSON_EXTRACT 함수로 특정 값 추출
SELECT
name,
JSON_EXTRACT(attributes, '$.screen_size') AS screen_size,
JSON_EXTRACT(attributes, '$.storage') AS storage,
JSON_EXTRACT(attributes, '$.color') AS color
FROM product_json
WHERE category = '스마트폰';

- "JSON_EXTRACT" 함수를 사용하면 JSON에서 특정 경로의 값을 추출할 수 있다.
- 경로는 "$"로 시작하며, "."로 키를 지정한다.
"JSON_EXTRACT"는 저장한 타입을 그대로 꺼낸다. 숫자는 숫자 타입으로, 불리언은 불리언 타입으로 조회한다. 주의할 점은 "JSON_EXTRACT"로 문자열을 추출하면 큰따옴표가 포함된다는 것이다. "JSON_EXTRACT"는 추출한 값의 JSON 데이터 타입(형식)을 그대로 유지하여 반환하기 때문이다. JSON 표준에서 문자열은 반드시 큰따옴표로 감싸져 있어야 하므로, 결과에도 큰따옴표가 포함된다.
JSON_QUERY, JSON_EXTRACT
MySQL에서는 표준 SQL과 달리 JSON_QUERY라는 함수명이 존재하지 않는다. 대신 "JSON_EXTRACT"가 그 역할을 수행한다.
"->" 연산자 (JSON_EXTRACT 축약형)
SELECT
name,
attributes->'$.screen_size' AS screen_size,
attributes->'$.storage' AS storage,
attributes->'$.color' AS color
FROM product_json
WHERE category = '스마트폰';

JSON_VALUE 함수로 특정 값 추출
SELECT
name,
JSON_VALUE(attributes, '$.screen_size') AS screen_size,
JSON_VALUE(attributes, '$.storage') AS storage,
JSON_VALUE(attributes, '$.color') AS color
FROM product_json
WHERE category =
'스마트폰';

- "JSON_VALUE" 함수는 추출한 값에서 따옴표를 제거하고, 별도의 타입을 지정하지 않으면 문자열로 반환한다.
- screen_size, storage, color 모두 문자열 타입으로 반환된다.
JSON_VALUE와 반환 타입 지정
"JSON_VALUE"의 강력한 기능 중 하나는 반환되는 데이터의 타입을 우리가 원하는 대로 지정할 수 있다는 점이다. "JSON_VALUE" 함수에 별도의 타입을 지정하지 않으면 문자열로 반환한다. 숫자 연산을 하거나 크기를 비교할 때, 데이터 타입이 맞지 않으면 엉뚱한 결과가 나올 수 있다. 이때 "RETURNING" 절을 사용하여 데이터 타입을 명시적으로 변환해 준다.
SELECT
name,
JSON_VALUE(attributes, '$.storage') AS storage_text,
JSON_VALUE(attributes, '$.storage' RETURNING UNSIGNED) AS storage,
JSON_VALUE(attributes, '$.screen_size' RETURNING DECIMAL(4,2)) AS
screen_size
FROM product_json
WHERE category = '스마트폰'
ORDER BY storage DESC;

- "RETURNING"을 지정하지 않음: 문자로 반환한다.
- 여기서 반환한 "storage_text"의 256은 숫자가 아니라 문자열 "256"이다.
- "RETURNING UNSIGNED": 추출한 값을 양의 정수(0과 양수)로 변환한다.
- 이제 "storage"는 문자열 "256"이 아니라 숫자 256이 된다.
- "RETURNING DECIMAL(3,2)": 추출한 값을 소수점 둘째 자리까지 있는 숫자로 변환한다.
이렇게 타입을 지정하면 SQL 내부에서 대소 비교(>, < )를 하거나 정렬(ORDER BY)을 할 때, 문자열 기준이 아닌 숫자 기준으로 정확하게 동작하게 된다.
데이터 타입
"JSON_VALUE" 함수의 "RETURNING" 절 뒤에 사용할 수 있는 데이터 타입들은 MySQL의 "CAST()" 함수에서 지원하는 타입들과 거의 동일하다. 실무에서 자주 사용하는 타입들을 정리해두었으니 참고하자.
1. 숫자형 (Numeric Types)
| 타입 | 설명 | 사용 예시 |
| UNSIGNED [INTEGER] | 양수 정수 (0 포함) |
RETURNING UNSIGNED |
| SIGNED [INTEGER] | 음수 포함 정수 | RETURNING SIGNED |
| DECIMAL[(M[,D])] | 고정 소수점 (정확한 계산, 금액 등) | RETURNING DECIMAL(10,2) |
| FLOAT | 부동 소수점 (단정밀도) | RETURNING FLOAT |
| DOUBLE | 부동 소수점 (배정밀도) | RETURNING DOUBLE |
2. 날짜 및 시간형 (Date and Time Types)
| 타입 | 설명 | 사용 예시 |
| DATE | 날짜 ("YYYY-MM-DD") | RETURNING DATE |
| DATETIME | 날짜 + 시간 |
RETURNING DATETIME |
| TIME | 시간 (HH:MM:SS) | RETURNING TIME |
3. 문자열 및 바이너리 (String and Binary Types)
| 타입 | 설명 | 사용 예시 |
| CHAR[(N)] | 고정 길이 문자열 | RETURNING CHAR(50) |
| BINARY[(N)] | 이진 문자열 (대소문자 구분 확실) | RETURNING BINARY(16) |
"->>" 연산자 (따옴표 제거)
앞서 "JSON_VALUE"를 사용하면 따옴표가 제거된 값을 얻을 수 있다고 했다. 하지만 매번 "JSON_VALUE(col, '$.key')"라고 길게 쓰는 것은 번거롭다. 이를 위해 "->>" 연산자가 제공된다. "->>"는 "JSON_VALUE"의 축약형이다. 추출한 값에서 따옴표를 제거하고 문자열로 반환한다. 단, 이 연산자는 타입을 지정(RETURNING)할 수는 없으므로 단순히 문자 타입으로 값을 꺼낼 때 사용한다
SELECT
name,
attributes->>'$.screen_size' AS screen_size,
attributes->>'$.storage' AS storage,
attributes->>'$.color' AS color
FROM product_json
WHERE category = '스마트폰';

- 결과를 보면 color 값에 따옴표가 제거된 것을 확인할 수 있다. 단순 조회가 목적이라면 "->>" 연산자를 가장 많이 사용하게 될 것이다.
- "->>"는 문자열 타입을 반환하므로 숫자나 불리언을 비교해야 한다면 "->" 연산자를 사용해야 한다.
배열 요소 접근
JSON 데이터가 배열(Array) 형태라면 어떻게 접근해야 할까? 프로그래밍 언어와 마찬가지로 인덱스(Index)를 사용한다. 인덱스는 0부터 시작한다.
SELECT
name,
attributes->>'$.ports[0]' AS first_port,
attributes->>'$.ports[1]' AS second_port
FROM product_json
WHERE category = '노트북';

JSON 조건 검색
숫자 비교
-- 화면 크기가 6인치 이상인 제품 조회
SELECT name, attributes->'$.screen_size' AS screen_size
FROM product_json
WHERE attributes->'$.screen_size' >= 6;

문자열 비교
-- 블랙 색상 제품 조회
SELECT name, attributes->>'$.color' AS color
FROM product_json
WHERE attributes->>'$.color' LIKE '%블랙%'

불리언 비교
-- 노이즈 캔슬링 지원 제품 조회
SELECT name, category
FROM product_json
WHERE attributes->'$.noise_canceling' = true;

JSON_CONTAINS로 배열 검색
-- USB-C 포트가 있는 노트북 조회
SELECT name, attributes->>'$.ports' AS ports
FROM product_json
WHERE JSON_CONTAINS(attributes->'$.ports', '"USB-C"');

- 배열에 특정 값이 포함되어 있는지 확인할 때는 "JSON_CONTAINS"를 사용한다.
- "JSON_CONTAINS"의 두 번째 인자는 JSON 형식이어야 하므로 문자열은 '"USB-C"'처럼 큰따옴표를 포함해야 한다.
JSON_CONTAINS_PATH로 키 존재 확인
JSON_CONTAINS_PATH(json_doc, 'one' | 'all', path[, path] ...)
- one : 여러 path 중 하나라도 존재하면 TRUE
- all : 지정한 모든 path가 존재해야 TRUE
-- s_pen 속성이 있는 제품 조회
SELECT name, category, attributes->>'$.s_pen' AS s_pen
FROM product_json
WHERE JSON_CONTAINS_PATH(attributes, 'one', '$.s_pen');

'데이터 베이스 > 설계 2편, 실무에서 반드시 마주치는 9가지 설계 패턴' 카테고리의 다른 글
| Ch08. JSON 설계 - JSON 활용 (0) | 2026.08.01 |
|---|---|
| Ch08. JSON 설계 - JSON 사용하기(2) (0) | 2026.08.01 |
| Ch08. JSON 설계 - JSON 문법 (0) | 2026.07.29 |
| Ch08. JSON 설계 - EAV의 한계와 JSON 필요성 (0) | 2026.07.29 |
| Ch07. 동적 설계 EAV - EAV의 장단점과 주의사항 (0) | 2026.07.27 |