
Audit Log 조회 페이지에서 검색어 필터가 정상적으로 동작하지 않는 문제가 발생했다. 프론트엔드에서는 정상적으로 keyword 파라미터가 전달되고 있었고, DB에서도 동일한 SQL을 실행하면 결과가 정상적으로 조회되었다.
하지만 Spring Boot 서버에서는 검색 요청 시 예외가 발생하면서 조회가 실패하고 있었다. 서버 로그를 확인한 결과 Hibernate 내부에서 Query 생성 중 예외가 발생하고 있었다.
Hibernate 로그를 확인하면 다음과 같은 오류가 발생하고 있었다.
FunctionArgumentException: Parameter 1 of function 'lower()' has type 'STRING', but argument is of type 'java.lang.String' mapped to 'CLOB'
즉 Hibernate에서 lower() 함수가 CLOB 타입 컬럼에 적용되면서 쿼리 생성 단계에서 예외가 발생하고 있었다.
AuditLog 엔티티의 detailJson 필드는 다음과 같이 저장되어 있었다.
@Lob
@Column(name = "detail_json")
private String detailJson;
@Lob 가 적용된 String 필드는 Hibernate에서 CLOB 타입으로 매핑된다.
기존 검색 로직에서는 모든 문자열 필드에 lower() 를 적용하고 있었다.
Expression<String> descriptionExpr = cb.lower(cb.coalesce(root.get("description"), ""));
Expression<String> usernameExpr = cb.lower(cb.coalesce(root.get("username"), ""));
Expression<String> detailJsonExpr = cb.lower(cb.coalesce(root.get("detailJson"), ""));
하지만 Hibernate 6에서는 CLOB 타입에 lower() 적용이 불가능하다.
따라서 detailJson 에 lower() 를 적용하는 순간 Hibernate Query 생성 단계에서 예외가 발생하고 결과적으로 검색 API 전체가 실패하고 있었다.
해결 방법은 단순하다.
lower() 사용CLOB 컬럼(detailJson) → lower() 제거 JSON 내부 검색은 대소문자 구분 없이 대부분 동작하므로 lower() 없이 like 검색만 적용하도록 수정하였다.
String lowerLikeKeyword = "%" + normalizedKeyword + "%";
String rawLikeKeyword = "%" + rawKeyword + "%";
Expression<String> descriptionExpr = cb.lower(cb.coalesce(root.get("description"), ""));
Expression<String> usernameExpr = cb.lower(cb.coalesce(root.get("username"), ""));
Expression<String> ipAddressExpr = cb.lower(cb.coalesce(root.get("ipAddress"), ""));
Expression<String> entityIdExpr = cb.coalesce(root.get("entityId").as(String.class), "");
Expression<String> actionExpr = cb.lower(root.get("action").as(String.class));
Expression<String> entityTypeExpr = cb.lower(root.get("entityType").as(String.class));
Expression<String> detailJsonExpr = cb.coalesce(root.get("detailJson"), "");
predicates.add(
cb.or(
cb.like(descriptionExpr, lowerLikeKeyword),
cb.like(usernameExpr, lowerLikeKeyword),
cb.like(ipAddressExpr, lowerLikeKeyword),
cb.like(entityIdExpr, rawLikeKeyword),
cb.like(actionExpr, lowerLikeKeyword),
cb.like(entityTypeExpr, lowerLikeKeyword),
cb.like(detailJsonExpr, rawLikeKeyword)
)
);
수정 이후 다음과 같은 검색이 정상적으로 동작한다.
인 → 로그인 로그 조회a12312 → 사용자 로그인 로그 조회master → 관리자 로그인 로그 조회로그 → 로그인 / 로그아웃 기록 조회lower() 사용 불가@Lob 필드는 Query Function 사용 시 타입 주의 필요like 만으로도 충분히 처리 가능이번 문제는 검색 로직 자체의 문제가 아니라 Hibernate 타입 처리 방식 변경 때문에 발생한 이슈였다.
단순한 검색 기능처럼 보였지만 ORM 레벨에서의 타입 처리와 Query Function 제약을 이해하지 못하면 예상치 못한 예외가 발생할 수 있다는 것을 다시 한번 확인할 수 있었다.
특히 Hibernate 6에서는 이전 버전보다 Query Function 타입 검사가 훨씬 엄격해졌기 때문에 LOB 컬럼을 다룰 때는 반드시 주의가 필요하다.