[WebMvcTest][JPA] java.lang.IllegalArgumentException: JPA metamodel must not be empty

SEPTEMBER·2025년 5월 6일

< 작업 환경 >

IDE: IntelliJ
Spring Boot: 3.4.5
Java: 21

문제

Caused by: java.lang.IllegalArgumentException: JPA metamodel must not be empty

@WebMvcTest를 사용해서 Controller 단위 테스트를 진행하던 중에 JPA metamodel must not be empty 오류가 발생했다. 테스트 코드는 다음과 같다.

@ActiveProfiles("test")
@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper; // JSON 과 object 간에 직렬화/역직렬화를 도와줌

    @MockBean
    private ProductService productService;

    @DisplayName("신규 상품을 등록한다.")
    @Test
    void createProduct() throws Exception {
        // given
        ProductCreateRequest request = ProductCreateRequest.builder()
                .type(HANDMADE)
                .sellingStatus(SELLING)
                .name("카푸치노")
                .price(5000)
                .build();

        // when //then
        mockMvc.perform(
                post("/api/v1/products/new")
                .content(objectMapper.writeValueAsString(request))
                .contentType(MediaType.APPLICATION_JSON)
        )
                .andDo(print()) // 로그를 자세하게 찍어 볼 수 있다
                .andExpect(status().isOk());
    }

테스트 코드를 보면 @WebMvcTest를 사용하고 있고 ProductService를 @MockBean으로 주입하고 있기 때문에 데이터베이스나 엔티티 클래스는 전혀 사용되지 않는 구조라는 걸 알 수 있다. 따라서 처음에는 "JPA를 쓰지도 않았는데 왜 이런 에러가 뜨지?"라는 의문이 들었다.

오류 로그는 제일 마지막 줄을 보는게 국룰이라지만 이번엔 마지막 줄만 봐서는 상황을 이해하기가 힘들었기 때문에 로그 전체를 훑어봤다. 그리고 아래 로그를 통해 힌트를 얻을 수 있었다.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument

로그에 따르면 'jpaAuditingHandler' 빈을 생성하는 과정에서 문제가 생긴 것이었다. 아마도 엔티티 생성일과 수정일을 필드로 가진 BaseEntity와 관련된 문제인 것 같았다.

해결

문제의 원인은 애플리케이션 메인 클래스에 설정된 @EnableJpaAuditing 때문이었다.

@EnableJpaAuditing
@SpringBootApplication
public class CafekioskApplication {
    public static void main(String[] args) {
        SpringApplication.run(CafekioskApplication.class, args);
    }
}

@EnableJpaAuditing은 JPA 엔티티에서 @CreatedDate, @LastModifiedDate 같은 기능을 사용하기 위해 필요한 설정이다. 이 어노테이션을 위 코드처럼 메인 클래스에 작성하면 @WebMvcTest처럼 JPA를 쓰지 않는 슬라이스 테스트에도 영향을 주게 된다.

테스트를 실행하면 @EnableJpaAuditing도 같이 활성화되면서 Spring이 JpaAuditingHandler를 빈으로 등록하려고 한다. 이때 JpaAuditingHandler는 내부적으로 JPA 메타모델(Metamodel) 정보를 필요로 하게 되는데, 문제는 @WebMvcTest는 오직 Controller와 관련된 빈만 로딩하기 때문에 JPA 관련된 Entity 클래스들은 스캔되지 않는다는 것이다. 이 상황에서 JpaAuditingHandler는 "스캔된 엔티티가 없는데 왜 나를 쓰려 해?"라고 하며 JPA metamodel must not be empty 오류를 발생시키는 것이다.

정리하면,

  • @WebMvcTest는 메인 클래스를 사용해서 테스트 컨텍스트를 구성함
  • 이때 @EnableJpaAuditing도 같이 활성화되면서 Spring이 JpaAuditingHandler를 빈으로 등록하려고 함
  • 하지만 @WebMvcTest는 엔티티 정보를 포함하지 않음 → JPA Metamodel must not be empty 오류 발생

해결 방법은 생각보다 간단하다. @EnableJpaAuditing을 메인 어플리케이션에서 분리하기만 하면 된다.

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}

이렇게 별도 클래스로 분해주면 테스트에서는 이 설정 클래스가 스캔되지 않는다. 왜냐하면 @WebMvcTest는 슬라이스 테스트이기 때문에 컨트롤러 관련 클래스만 로딩하기 때문이다. JpaAuditingConfig는 이 범위에 포함되지 않기 때문에 등록되지 않고 결과적으로 JPA 관련 오류도 발생하지 않게 된다.

0개의 댓글