spring-webmvc 에서 SpringBoot로 단계별로 전환하기
전환 사유
- 대부분의 가이드, docs, 자료가 SpringBoot를 전제하고 있어 mvc 프로젝트 유지보수 시 불필요하게 리소스가 낭비되는 부분이 있음.
- @MockBean, @SpyBean 등 SpringBoot의 TC 지원 애너테이션을 사용 할 수 없어 TC 작성 효율이 떨어지고 보일러플레이트 작성에 대한 진입장벽 높음.
- SpringBoot로 전환하는게 장기적으로 유지보수비용이 더 세이브 될 것 같아 전환 결정.
As-is
- spring-webmvc 4.3.4.RELEASE
- 외장 tomcat
- maven
- jsp 코드 다수
- 폴더 기반 deploy (war 사용하지 않고 디렉터리 전체를 배포)
- 폴더 전체 배포는 아무래도 node_modules 같은 디렉터리는 디플로이 시 제외해야 하는 등 신경써야 하는 부분이 생겨서, 애플리케이션 구동 시 필요한 모든 정보를 war에 넣고 war만 배포하는게 유지보수 하기 더 편하다.
단계별로 전환하기
- jsp 페이지가 많이 존재하는데, 스프링부트 내장 톰캣은 jsp를 빌트인으로 지원하지 않음.
- jasper를 쓰면 내장 톰캣에서 jsp 쓸 수 있다지만, 이런 변수를 늘리면서 무리하게 executable jar 사용할 필요는 없어보임.
- 또는 jsp 페이지를 모두 Thymeleaf 등으로 전환하는 방법도 있겠지만, 무리해서 일시에 전환하기 보다는 점진적으로 진행 할 수 있도록 전환 계획을 아래와 같이 수립.
---
- 의존성 spring mvc → springboot로 변경하고 설정 잡기 (외장 tomcat, jsp, 폴더기반 배포는 유지)
- jsp를 Thymeleaf 등으로 마이그 (시간 날 때 진행)
- 외장 tomcat 걷어내고 내장 tomcat 사용. jar 패키징 및 배포
의존성 spring mvc → springboot로 변경하고 설정 잡기
1-1. 의존성 세팅
<groupId>dev.umbum</groupId>
<artifactId>web</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>my-jsp-web</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/>
</parent>
... 기존 의존성들 충돌 안나게끔 정리
- 외장톰캣, war(폴더 기반 배포) 사용 할 것이기 때문에 embedded-tomcat, jasper 의존성 필요없음.
- 빌드 한 번 돌려본다.
1-2. WebApplicationInitializer 전환
- 기존 spring-webmvc 프로젝트에는 xml 기반 설정이라면 web.xml 설정파일이, java 기반 설정이라면 WebApplicationInitializer를 직접 구현한 구현체가 있을텐데 이를 마이그 해주어야 한다.
- java 기반 설정 (WebApplicationInitializer) 되어 있는 경우 예시 (아래 코드)
public class WebApplicationInitializerImpl implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
// servlet listener 등록
// servlet filter 등록
// property source 설정
// spring.profiles.active 설정
기타 다양한 설정
}
}
- spring-webmvc의 애플리케이션 진입점은 WebApplicationInitializer.onStartup 이므로, 이 부분을 제거해주고, SpringBoot의 애플리케이션 진입점인 @SpringBootApplication 를 설정해준다.
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
... 기타 수동으로 설정하고 있기 때문에 AutoConfig 타면 안되는 항목들
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 기존 구현체에서 수행하던 ServletFilter, ServletListener, PropertySource 등의 설정은 모두 삭제하고 아래와 같이 마이그해준다.
- Filter와 Listener는 SpringBoot의 Bean 등록 방식으로 변경.
- PropertySource 설정은 application.properties에서.
@Configuration
public class ServletConfig {
@Bean
public ServletListenerRegistrationBean<LogbackShutdownListener> logbackShutdownListener() {
ServletListenerRegistrationBean<LogbackShutdownListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(new LogbackShutdownListener());
return registrationBean;
}
@Bean
public FilterRegistrationBean<XssEscapeServletFilter> xssEscapeServletFilter(){
FilterRegistrationBean<XssEscapeServletFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new XssEscapeServletFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setMatchAfter(true);
return registrationBean;
}
}
spring.profiles.active=@environment@
spring.config.import=classpath:common.properties, classpath:web.properties
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
# https://www.baeldung.com/spring-mvc-content-negotiation-json-xml#basics-1 참고
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
기존에 maven 사용하고 있었기 때문에 profile은 maven에서 변수로 넘겨받아 @environment@로 세팅.
[Spring] profile로 alpha, beta, real 빌드 구분하기
- 마지막으로 기존 WebApplicationInitializer의 구현체를 아예 삭제해주고, SpringBootServletInitializer를 상속한 클래스를 만들어준다.
- 참고로 boot에서 제공하는 SpringBootServletInitializer 도 WebApplicationInitializer의 구현체다.
- SpringBootServletInitializer docs를 읽어보면 Ordered 인터페이스를 구현해주면 기존 구현체를 삭제 안하고 둘 다 쓸 수 있어 보이긴 하지만, 굳이 같은 용도의 설정 클래스를 두개로 유지하기 보다는 하나로 합치는게 깔끔해보인다.
- SpringBootServletInitializer 란 무엇이고 왜 상속받고 있는가?
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
여기까지 하고 기존처럼 외장 tomcat에서 애플리케이션 실행해본다. 서버 잘 뜨면 다음으로.
1-3. WebMvcConfigurer 최신화
- EnableWebMvc와 WebMvcConfigurer 의 관계
- 기존에 WebMvcConfigurerAdapter를 상속하던 Bean이 있다면 이를 WebMvcConfigurer 구현으로 바꿔준다.
- @EnableWebMvc는 삭제해준다.
- viewResolver 설정, argumentResolver 설정을 최신화 해준다.
public class WebMvcConfigurerImpl implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/{경로잡아준다}", ".jsp");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(myArgumentResolver);
}
1-4. /build/ 로 접근했을 때 webapp/build/index.html 반환하도록 매핑하기
- /build/#/... 로 접근해도 반환되던 react 페이지가 반드시 /build/index.html#/... 로 접근해야만 반환된다면 아래 설정 넣어준다.
- https://stackoverflow.com/questions/27381781/java-spring-boot-how-to-map-my-app-root-to-index-html
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/build/").setViewName("forward:/build/index.html");
}
1-5. boot로 변경하면서 spring 4 -> 5로 버전업 했다면 호환성 체크
- spring-webmvc 4.3.x와 5.x.x의 차이 - HttpMessageNotWritableException 참고
- jackson 2.12.0 부터 LocalDate 계열 (de)serialize를 위해 반드시 JavaTimeModule을 ObjectMapper에 추가 해 주어야 함. (jackson 버전이 올라갔고 & ObjectMapper를 직접 생성해서 사용하고 있다면) 확인 필요. - [Java] Jackson ObjectMapper Serialization
전환 1단계 끝.
생각 보다 간단하게 전환 할 수 있다.
이제 maven을 gradle로 전환한다거나, jsp를 걷어낸다거나, 내장 tomcat을 사용한다거나 하는 것들을 차차 진행하면 된다.
추가 내용 참고 - https://www.baeldung.com/spring-boot-migration
'Java Stack > Spring' 카테고리의 다른 글
spring-webmvc 4.3.x와 5.x.x의 차이 - HttpMessageNotWritableException (0) | 2023.04.03 |
---|---|
[Spring] EventListener (0) | 2022.04.17 |
테스트 클래스를 일정 수 이상 묶어서 실행하면, 어느 정도 실행하다가 갑자기 JDBC Connection을 무한히 대기하는 현상 (0) | 2022.01.12 |
[Spring] DB 관련 : Mybatis CustomTypeHandler (0) | 2021.03.31 |
[Spring] WebClient (0) | 2021.03.14 |