새발블로그

[Spring] MyBatis에서 Enum 타입 안전하게 매핑하기 본문

Server/Spring

[Spring] MyBatis에서 Enum 타입 안전하게 매핑하기

EUG 2025. 10. 7. 15:56

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