새발블로그
[Spring] MyBatis에서 Enum 타입 안전하게 매핑하기 본문
1. 문제 상황
처음에 MyBatis에서 단일 Enum만 매핑할 때는 크게 문제가 없었는데,
프로젝트가 커지고 Gender, OAuth2Provider, ProductType, RiskLevel 등 여러 개의 Enum을 DB 컬럼과 연결하다 보니 문제가 생겼다.
- MyBatis 기본 동작: Enum.name() / Enum.ordinal()로 자동 매핑 가능
- 문제: Enum이 여러 개일 경우, 어떤 Enum 클래스인지 정확히 구분하지 못해서 ClassCastException 같은 에러 발생
- 해결책: TypeHandler를 Enum마다 따로 구현해 주기
2. 해결책: BaseTypeHandler 상속받아 구현
예를 들어 ProductType 이라는 Enum을 DB의 VARCHAR 컬럼(product_type)과 매핑하려면 다음과 같이 구현한다.
package com.banklab.product.handler;
import com.banklab.product.domain.ProductType;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
public class ProductTypeHandler extends BaseTypeHandler<ProductType> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, ProductType parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.name()); // Enum → String
}
@Override
public ProductType getNullableResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return value != null ? ProductType.valueOf(value) : null; // String → Enum
}
@Override
public ProductType getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return value != null ? ProductType.valueOf(value) : null;
}
@Override
public ProductType getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return value != null ? ProductType.valueOf(value) : null;
}
}
핵심은 setNonNullParameter()와 getNullableResult()에서 Enum ↔ String 변환 로직을 명확히 해주는 것이다.
3. MyBatis 설정: mybatis-config.xml
Enum 타입이 여러 개라면, 각 Enum마다 typeHandler를 등록해 줘야 한다.
<typeHandlers>
<typeHandler handler="com.banklab.member.handler.GenderTypeHandler"
javaType="com.banklab.member.domain.Gender"/>
<typeHandler handler="com.banklab.security.oauth2.handler.OAuth2ProviderTypeHandler"
javaType="com.banklab.security.oauth2.domain.OAuth2Provider"/>
<typeHandler handler="com.banklab.product.handler.ProductTypeHandler"
javaType="com.banklab.product.domain.ProductType"/>
<typeHandler handler="com.banklab.risk.handler.RiskLevelHandler"
javaType="com.banklab.risk.domain.RiskLevel"/>
</typeHandlers>
이렇게 등록해 주면, MyBatis가 ResultMap이나 insert/update 시점에 알아서 변환해 준다.
4. Mapper XML에서 활용하기
resultMap에서 특정 컬럼이 Enum 타입이라면, typeHandler를 명시적으로 지정할 수도 있다.
<resultMap id="productRiskRatingMap" type="com.banklab.risk.domain.ProductRiskRating">
<id property="id" column="id"/>
<result property="productType" column="product_type"
typeHandler="com.banklab.product.handler.ProductTypeHandler"/>
<result property="riskLevel" column="risk_level"
typeHandler="com.banklab.risk.handler.RiskLevelHandler"/>
</resultMap>
typeHandler를 지정해주면 어떤 Enum 변환기를 쓸지 확실히 고정된다.
5. 장점
- DB 컬럼(VARCHAR)과 자바 Enum을 안전하게 매핑 가능
- 여러 Enum 클래스가 있어도 구분 문제 없이 처리 가능
- 유지보수가 쉬움: 각 Enum별 변환 로직을 독립적으로 관리
6. 마무리
MyBatis에서 Enum을 단순히 name()으로 쓰면 작은 프로젝트에서는 문제 없지만,
규모가 커지고 Enum이 여러 개 섞이면 반드시 TypeHandler를 만들어 관리하는 것이 안전하다.
결국 핵심은 "MyBatis가 어떤 Enum 클래스와 매핑해야 하는지"를 명확히 알려주는 것.
각 Enum별로 TypeHandler를 작성하고, mybatis-config.xml에 등록해주면 된다
'Server > Spring' 카테고리의 다른 글
| [Spring] Spring Batch + Scheduler (0) | 2025.10.07 |
|---|---|
| [Spring] WebSocket + STOMP (0) | 2025.09.22 |
| [Spring] AOP (Aspect Oriented Programming) (0) | 2025.09.22 |
| [Spring] 직렬화와 역직렬화 (0) | 2025.09.22 |
| [Spring] 파일 업로드 & 다운로드 (0) | 2025.09.22 |