Spring MVC + Thymeleaf 테스트 시 ApplicationContext 로드 실패 트러블슈팅

섭정이·2025년 4월 15일

Spring MVC + Thymeleaf 테스트 시 ApplicationContext 로드 실패 트러블슈팅

1. 문제 상황 요약

  • Spring MVC 프로젝트에서 AppConfigThymeleaf 관련 설정을 직접 구성.
  • 실제 WAS 실행 시 페이지 정상 출력.
  • 그러나 테스트 코드에서는 ApplicationContext 로딩 중 예외 발생:
java.lang.IllegalStateException: No ServletContext set

2. 프로젝트 설정 개요

AppConfig.java

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.chilluminati.chillstock")
public class AppConfig implements WebMvcConfigurer {

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML");
        resolver.setCharacterEncoding("UTF-8");
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        return engine;
    }

    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setCharacterEncoding("UTF-8");
        return resolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }
}

테스트 코드

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
class AppConfigTest {
    @Autowired
    private SpringTemplateEngine templateEngine;

    @Test
    void thymeleafEngineIsNotNull() {
        assertNotNull(templateEngine);
    }
}

3. 테스트 실패 이유

1) Bean 등록 자체는 문제 없음

혹시 테스트코드에서 빈으로 등록이 안되었나 했지만 AppConfig@Configuration이므로 templateResolver(), templateEngine()는 테스트 환경에서도 정상적으로 등록되는것을 확인했다.


2) 그럼 왜 안된걸까..?

테스트 코드에 있던 @EnableWebMvc가 문제였다.

  • @EnableWebMvcWebMvcConfigurationSupport를 통해 Spring MVC 관련 설정(예: HandlerMapping, ResourceHandlerMapping 등)을 자동 등록한다.
  • 이 구성에서 ServletContext가 필요하지만 테스트 환경에서는 기본적으로 존재하지 않았는데 이 때문에 No ServletContext set 예외가 발생했던 것이다.

해결 방법

@WebAppConfiguration 추가

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
@WebAppConfiguration
class AppConfigTest {
    ...
}
  • @WebAppConfiguration은 테스트 컨텍스트를 WebApplicationContext로 전환
  • 내부적으로 MockServletContext를 만들어 Spring에게 전달
  • MVC 설정이 필요로 하는 ServletContext를 공급
  • 따라서 resourceHandlerMapping 같은 MVC 구성도 정상 작동

실제 서버에서는 왜 문제없이 동작했을까?

  • 실제 서버 실행 시에는 톰캣, 제티 등 서블릿 컨테이너가 자동으로 ServletContext를 생성하고 Spring에 전달한다.
  • 하지만 테스트 환경은 별도의 서블릿 컨테이너가 없으므로 MockServletContext가 필요했던 것이다.

참고: 순수한 templateEngine만 테스트하고 싶을 때

  • MVC 설정 없이, 단순히 Thymeleaf 관련 빈만 테스트하려면 AppConfig에서 @EnableWebMvc를 제거한 별도 설정 클래스를 만들어 테스트에 주입할 수도 있다.
@Configuration
public class ThymeleafOnlyConfig {
    @Bean
    public SpringResourceTemplateResolver templateResolver() { ... }

    @Bean
    public SpringTemplateEngine templateEngine() { ... }
}
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ThymeleafOnlyConfig.class)
class TemplateEngineTest {
    @Autowired
    SpringTemplateEngine engine;

    @Test
    void engineNotNull() {
        assertNotNull(engine);
    }
}

결론

구분설명
실제 서버 실행 시서블릿 컨텍스트 자동 제공 → MVC 구성 정상
테스트 실행 시ServletContext 없음 → @WebAppConfiguration 필수
테스트 실패 원인@EnableWebMvc로 생성되는 MVC 구성 요소가 서블릿 컨텍스트 필요
해결테스트 클래스에 @WebAppConfiguration 추가
대안Thymeleaf만 테스트할 경우 MVC 설정 분리 가능

04.15 18:51 2차 수정

난 템플릿 엔진만 테스트 하고 싶은데 해당 설정이 webmvc에 들어가 있어서 테스트 코드가 @EnableWebMvc 에 종속되는 결과를 낳았다.

따라서 thymeleaf관련 설정 클래스를 따로 빼고 해당 클래스를

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { ThymeleafConfig.class };// DB 설정이 따로 있다면 여기에 추가 기타 컨픽도 추가
    }

에 추가하여 구조를 바꾸고 테스트코드도 다음과 같이 사용할 수 있었다.

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ThymeleafConfig.class)
class WebMvcConfigTest {
    @Autowired
    private SpringTemplateEngine templateEngine;

    @Test
    public void thymeleafEngineIsNotNull() {
        assertNotNull(templateEngine);
    }

}

실제로 이렇게 구조화 하는게 추가적으로 의존성이 추가될때 보기도 편하고 유지보수 하는데도 유리할 것이라고 판단하여 앞으로 mtbatis 나 히카리 등 추가할떄도 이 방법을 쓰기로 하였다.

profile
우직하게

0개의 댓글