티스토리 뷰

 

2020/05/13 - [framework/Springboot] - spring 프로젝트에서 springboot 프로젝트로 migration 하기 (1) - pom.xml

 

위의 글에서 springboot 전환의 첫 걸음인 pom.xml 전환을 해 보았다.

 

그다음은 web.xml 파일을 제거하는 것이다. 추세가 xml config에서 java config로 전환중인데 이런 web.xml 도 java config로 전환을 할 수 있다. (개인적으로는 익숙해서 그런지 xml config가 더 가독성이 좋은것 같다..)

 

AS-IS 프로젝트의 web.xml 은 이런 모습이었다.

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<display-name>ABC System</display-name>

	<context-param>
		<param-name>logbackConfigLocation</param-name>
		<param-value>classpath:logback/logback.xml</param-value>
	</context-param>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/application-context/*-context.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
	<listener>
		<listener-class>abc.login.SessionAttributeListener</listener-class>
	</listener>
	<filter>
		<filter-name>characterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>characterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>requestContextFilter</filter-name>
		<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>requestContextFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>EnvironmentSetupFilter</filter-name>
		<filter-class>abc.filter.EnvironmentSetupFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>EnvironmentSetupFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/web-application-context/dispatcher-context.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
	</servlet-mapping>
	<session-config>
		<session-timeout>60</session-timeout>
	</session-config>
	<jsp-config>		
		<jsp-property-group>
			<url-pattern>*.jsp</url-pattern>
			<page-encoding>UTF-8</page-encoding>
			<scripting-invalid>false</scripting-invalid>
			<include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
		</jsp-property-group>
		<jsp-property-group>
			<url-pattern>*.jspf</url-pattern>
			<page-encoding>UTF-8</page-encoding>
			<scripting-invalid>false</scripting-invalid>
			<include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
		</jsp-property-group>
	</jsp-config>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<error-page>
		<error-code>404</error-code>
		<location>/404.html</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/500.html</location>
	</error-page>
</web-app>

 

이렇게 많은 일을 해주는 web.xml 을 지운다고? 이 기능들은 다 각각 어떻게 집어 넣어야 할까.. 이 부분이 이번 전환을 하면서 가장 난감하고 어려웠던 부분이었던것 같다. 하지만 다 길은 있다. 

 


<context-param>

 

위의 context-param 부분부터 하나씩 전환을 해 나가도록 하자.

<context-param>
          <param-name>logbackConfigLocation</param-name>
          <param-value>classpath:logback/logback.xml</param-value>
</context-param>
<context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/application-context/*-context.xml</param-value>
</context-param>

 

현재 ABC 프로젝트에는 이렇게 두개의 context-param 설정이 들어가 있다. 

logbackConfigLocation은 나중에 springboot로 전환하기 로그편에서 다룰것이지만 springboot는 logback을 기본 logging framework로 사용하고 있기 때문에 간편하게 설정할수 있도록 지원을 하고 있다. 따라서 위와 같이 사용하지 않고 logback.xml 파일의 위치나 경로를 변경하고 싶다면 application.properties 파일에 다음과 같이 추가하면 된다. 

 

application.properties

logging.config=classpath:logback/logback.xml

 

 

contextConfigLocation에 대한 설정은 springboot 시동 클래스(eg. Application.java) 에서 설정을 할 수 있다. 또한 servlet 쪽에 정의된 contextConfigLocation도 이곳에서 같이 정의해서 사용하면 된다. 

 

Application.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration.class, SpringBootWebSecurityConfiguration.class})
@ImportResource(value= {"classpath:spring/application-context/common-context.xml", 
			"classpath:spring/application-context/dataaccess-context.xml",
			"classpath:spring/application-context/framework-context.xml",
			"classpath:spring/application-context/security-context.xml",
			"classpath:spring/web-application-context/dispatcher-context.xml"
})
public class Application {
    public static void main(String[] args) {
    	SpringApplication application = new SpringApplication(Application.class);
    	application.run(args);
    }
}

 

web.xml에 정의한 contextConfigLocation은 위와 같이 springboot main 클래스에서 @ImportResource를 통해 정의를 할 수 있다. (wildcard로 적용을 했을때는 안됐다.) 이 applicationContext도 장기적으로 봤을때는 모두 java config로 전환을 해야 하지만 일단은 시간이 없기에 그냥 기존의 xml config를 이용하도록 한다.

(2020.12.16 추가 xml config to java config)

 

위의 Application.java 파일은 springboot를 사용하기 위해서 필수적으로 있어야 하는 클래스이다. 앞장에서 유심히 봤다면 알겠지만 pom.xml에 spring-boot-maven-plugin 정의를 할때 이 main 클래스의 위치를 정의한적이 있었다. 이 클래스는 그 정의한 위치에 있어야 한다. @SpringBootApplication 으로 이 클래스의 역할을 정의하고 @EnableAutoConfiguration이 대부분의 설정을 자동으로 해준다. 이로 인해 web.xml 의 많은 부분들이 따로 설정하지 않아도 자동으로 설정이 되는것이다. springboot의 장점이 슬슬 보이기 시작한다. 

 


<listener>

<listener>
          <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
          <listener-class>abc.login.SessionAttributeListener</listener-class>
</listener>

 

 

HttpServletRequest를 인자로 가져오지 않고 직접 가지고 오기 위해서 필요한 RequestContextListener이다. 이 기능을 사용하지 않으면 추가하지 않아도 무방하다. 이와 유사한 spring에 내재된 listener는 다음과 같은 방식으로 추가해서 사용할 수 있다. 

 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextListener;

@Configuration
public class WebConfig implements WebMvcConfigurer {
	
	@Bean 
	public RequestContextListener requestContextListener(){
	    return new RequestContextListener();
	} 
}

 

하지만 spring의 DispatcherServlet과 RequestContextListener, RequestContextFilter는 모두 같은 일을 한다고 한다. 즉 위의 설정은 안해도 동작을 아마 잘 할것이다. 정리를 하며 많은 것을 배우는것 같다. 

 

아래의 SessionAttributeListener처럼 내가 직접 만든 listener를 등록할때도 마찬가지로 Component Scan만 가능하면 된다. 

@Component
public class SessionAttributeListener implements HttpSessionAttributeListener {
	public static ConcurrentHashMap<HttpSession, String> sessionMap = new ConcurrentHashMap<>();
    ... 후략...

 

이런 식으로 등록을 해 놓으면 잘 동작하는걸 확인할 수 있다. 

 


<filter>

처음이 어렵지 위의 listener 설정을 봤으면 filter의 설정도 유사하다는것을 알수 있다. 

 

        <filter>
		<filter-name>characterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>characterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>requestContextFilter</filter-name>
		<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>requestContextFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>EnvironmentSetupFilter</filter-name>
		<filter-class>abc.filter.EnvironmentSetupFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>EnvironmentSetupFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

이렇게 많은 필터가 있지만 이중에 spring에서 제공하는 filter들(CharacterEncodingFilter, HiddenHttpMethodFilter, FormContentFilter, RequestContextFilter)은 web.xml 처럼 내가 따로 정의하지 않더라도 자동으로 동작한다. 이래서 간결하게 웹 프로젝트를 구성할수 있다고 하는것 같다. 

 

이중 CharacterEncodingFilter에 대한 설정은 application.properties 에서 할수 있다. 

spring.http.encoding.charset=UTF-8
spring.http.encoding.enable=true
spring.http.encoding.force=true

 

그리고 spring security 관련 필터들도 spring-security-config dependency를 가지고 있으면 spring security 3.2 버전 이후부터는 직접 설정을 할 필요가 없다고 한다. 따라서 springSecurityFilterChain 부분은 과감히 삭제!

마지막으로 하나 남은 EnvironmentSetupFilter라는 Custom Filter가 있다. 이것은 다음과 같이 수정을 해준다.

 

@Component
public class EnvironmentSetupFilter implements Filter {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
			ServletException {

		HttpServletRequest hReq = (HttpServletRequest)request;        
    }
}


Filter를 implements 한 클래스라면 위의 listener처럼 Component Scan 만 가능하도록 만들어 주면 된다. Custom filter가 여러개 있으면 Filter Class도 여러개 만들어 주면 된다. 이 Filter 간의 순서를 정의하고 싶으면 다음과 같이 해준다.

 

@Component
@Order(1)
public class AFilter implement Filter {
..중략..
}

@Component
@Order(2)
public class BFilter implement Filter {
..중략..
}

 

할일이 많을것 같았는데 생각보다 할일이 별로 없었다.

 

[ServletRegistrationBean.onStartup:line190] - Mapping servlet: 'dispatcherServlet' to [/]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'characterEncodingFilter' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'httpPutFormContentFilter' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'environmentSetupFilter' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'org.springframework.security.filterChainProxy' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'org.springframework.security.web.access.intercept.FilterSecurityInterceptor#0' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0' to: [/*]
[FilterRegistrationBean.configure:line258] - Mapping filter: 'ajaxSessionTimeoutFilter' to: [/*]

 

다른 작업들이 더 진행이 되어야겠지만 일단 다 진행이 되었다고 가정하고 springboot를 기동하면 위와 같은 로그를 만날수 있다. spring 기본 filter 동작 -> Custom filter 동작 -> spring security filter 동작 -> spring security Custom filter 동작 순으로 filter가 알아서 잘 수행이 되었다. 

 


<servlet>

<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/web-application-context/dispatcher-context.xml</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

 

서블릿 관련 설정도 위에 말한대로 내부적으로 자동으로 설정이 되어 따로 설정할 필요는 없다

 

 


<session-config>

<session-config>
  <session-timeout>30</session-timeout>
</session-config>

 

이 부분은 application.properties 로 간단히 대체할 수 있다. 

 

server.session.timeout=1800

 

차이점은 위의 설정은 시간단위가 , 아래 설정은 이다. 

 


<jsp-config>

<jsp-config>		
  <jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
    <page-encoding>UTF-8</page-encoding>
    <scripting-invalid>false</scripting-invalid>
    <include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
  </jsp-property-group>
  <jsp-property-group>
    <url-pattern>*.jspf</url-pattern>
    <page-encoding>UTF-8</page-encoding>
    <scripting-invalid>false</scripting-invalid>
    <include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
  </jsp-property-group>
</jsp-config>

 

가장 애를 많이 먹은 부분이지만 결국은 해결하지 못했다. 이 jsp-config는 모든 jsp 파일에 공통된 내용을 넣어주기 위해서 들어있던 부분이다. 해본 과정들은 WebApplicationInitializer를 구현한 클래스에서 JspConfigDescriptor의 구현체를 호출해서 해결을 해보려고 했는데 실행 도중 예상치 못한 에러가 발생을 해서 시간 관계상 이 부분의 전환은 포기하게 되었다. 이 부분에 대해서는 더 알아보고 나중에 재시도를 해봐야 할듯 싶다.

 


<welcome-file-list>

<welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

 

listener 에서 잠깐 나왔던 WebConfig 라는 클래스는 WebMvcConfigurer 의 구현체이다. 구현해야 할 메소드들 중에

@Configuration
public class WebConfig implements WebMvcConfigurer{
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/").setViewName("forward:/index.html");
		
	}

 

addViewContollers 를 위와 같이 정의를 해주면 된다. 여기서 index.html의 위치는 public 혹은 static 등으로 정의되는 springboot 정적 resource 폴더 바로 하위에 위치시켜주면 된다. 

 

아니면 @RequestMapping("/") 을 정의하여 index.html 으로 forwarding 해주면 된다. 

 


<error-page>

<error-page>
  <error-code>404</error-code>
  <location>/404.html</location>
</error-page>
<error-page>
  <error-code>500</error-code>
  <location>/500.html</location>
</error-page>

에러 페이지에 대한 설정도 간단하다. 

 

나중에 구조 변경할때 다시 언급을 하겠지만 public 혹은 static 등으로 정의되는 springboot 정적 resource 폴더가 있는데 이 폴더의 error 라는 폴더 밑에 404.html, 500.html 파일을 생성해 놓으면 자동으로 인식을 한다. 

 

/src/main/resources/public(혹은 static)/error/404.html  이 경로에 에러파일을 생성해 놓으면 된다. 


쓰다보니 길어졌다. springboot 초보이다 보니 하나하나 전환하는데 어려움이 많았다. 위의 설명만으로 전환을 하려는 분들의 갈증을 해소를 다해주지는 못하겠지만 그래도 분명 필요로 하는 사람도 있을거라 생각하고 써본다. 

 

 

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (1) - pom.xml

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (2) - web.xml

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (3) - 파일 추가 및 변경

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (4) - logback

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (5) - 빌드 및 실행

spring 프로젝트에서 springboot 프로젝트로 migration 하기 (6) - XML config to JAVA

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31