MySQL의 네이티브 JSON 타입 활용
MySQL 5.7.8 버전부터 JSON 데이터 타입을 기본 제공한다. 이 기능을 통해 구조화된 데이터를 관계형 테이블 내에서 유연하게 저장하고 조작할 수 있다.
테이블 정의와 데이터 삽입
JSON 컬럼은 일반 컬럼처럼 선언 가능하며, 입력 시 유효한 JSON 형식만 허용된다.
CREATE TABLE product_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_code VARCHAR(50),
settings JSON NOT NULL
);
데이터 삽입 예시:
INSERT INTO product_config (product_code, settings)
VALUES ('P1001', '{"theme": "dark", "notifications": true, "language": "ko"}');
주요 JSON 함수 사용법
MySQL은 JSON 필드 접근 및 수정을 위한 내장 함수를 제공한다.
- 값 추출:
->는 JSON 문자열을 반환하고,->>는 일반 문자열로 변환하여 따옴표 없이 출력한다.
SELECT settings->'$.theme' FROM product_config; -- 결과: "dark"
SELECT settings->>'$.theme' FROM product_config; -- 결과: dark
JSON_SET() 함수로 기존 키 수정 또는 새 키 추가가 가능하다.UPDATE product_config
SET settings = JSON_SET(settings, '$.language', 'en', '$.timezone', 'UTC')
WHERE id = 1;
SELECT * FROM product_config
WHERE settings->>'$.notifications' = 'true';
JSON vs VARCHAR 비교
| 항목 | JSON | VARCHAR |
|---|---|---|
| 저장 형식 | 최적화된 바이너리 형식 | 일반 문자열 |
| 유효성 검사 | 삽입 시 자동 검증 | 무검증 |
| 내부 필드 접근 | 함수 지원 (->, JSON_EXTRACT) |
문자열 함수만 사용 가능 |
| 인덱싱 | 가상 컬럼 + 인덱스 가능 | 전체 컬럼 인덱스만 가능 |
| 성능 | 내부 필드 접근 빠름 | 매번 파싱 필요 |
기타 JSON 함수 요약
JSON_INSERT(): 존재하지 않는 키만 추가JSON_REPLACE(): 존재하는 키만 갱신JSON_REMOVE(): 특정 경로 삭제
실제 운영에서는 가장 유연한 JSON_SET() 사용이 권장된다.
MyBatis-Plus에서 JSON 객체 매핑
Java 애플리케이션에서 MySQL의 JSON 컬럼을 객체 형태로 사용하려면 타입 핸들러(TypeHandler)가 필요하다. MyBatis-Plus는 이를 위해 자동 ResultMap 생성 기능을 제공한다.
@TableName(autoResultMap = true)의 역할
이 어노테이션 속성은 MyBatis-Plus에게 해당 엔티티에 대해 명시적인 <resultMap>을 자동 생성하도록 지시한다. 이 맵은 컬럼과 자바 필드 간의 복잡한 변환 로직을 포함할 수 있다.
핵심 동작 원리
- autoResultMap이 true일 경우 MP는 엔티티 구조 기반으로 ResultMap을 생성
- 해당 맵은
@TableField(typeHandler=...)로 지정된 핸들러를 반영 - 쿼리 실행 시 직렬화/역직렬화 로직이 적용됨
실제 코드 예제
먼저 JSON에 매핑될 도메인 클래스를 정의한다:
public class UserPreference {
private String language;
private Boolean pushEnabled;
private String themeMode;
// getter/setter 생략
}
엔티티 클래스에서는 JSON 컬럼에 핸들러를 지정하고 autoResultMap을 활성화:
@TableName(value = "user_profiles", autoResultMap = true)
public class UserProfile {
@TableId
private Long id;
private String username;
@TableField(typeHandler = JacksonTypeHandler.class)
private UserPreference preferences;
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> roles;
}
글로벌 설정 (application.yml):
mybatis-plus:
type-handlers-package: com.example.handler
type-aliases-package: com.example.entity
사용 조건 및 주의사항
- 핸들러 필수: autoResultMap만 설정하면 무용지물. 반드시
@TableField(typeHandler=...)함께 사용 - 성능 고려: ResultMap 생성 오버헤드 존재. 단순 매핑에는 비활성화 권장
- XML 매퍼 사용 시: 직접 resultMap을 작성해야 하며, typeHandler를 명시해야 함
- 지원 버전: 3.3.1 이상에서 안정적으로 작동
활성화가 필요한 상황
| 사용 사례 | autoResultMap 필요 여부 |
|---|---|
| 기본 타입 (Long, String 등) | 불필요 |
| DB JSON → Java 객체 | 필수 |
| DB JSON → List/Map | 필수 |
| 암호화 저장 필드 | 필수 |
| 커스텀 직렬화 로직 | 필수 |
결론
MySQL의 JSON 컬럼을 효과적으로 활용하려면 데이터베이스 차원의 기능과 ORM 프레임워크의 매핑 기능을 적절히 결합해야 한다. MyBatis-Plus에서 autoResultMap = true는 JSON 타입을 자바 객체로 자연스럽게 바인딩하기 위한 핵심 설정이며, JacksonTypeHandler와 같은 직렬화 핸들러와 함께 사용되어야 완전한 기능을 발휘한다.