Servlet 3.0 – web.xml 없애기

운전 면허를 따긴 했는데, 차를 살 여력이 되지 않아 근 8년 만에 운전을 시작한 기분이다.(일명 장농면허) 자바 1.2 시절에 다뤄보고, 스프링 1.0 시절에 공부했다가 몇년 동안 쳐박혀 먼지만 쌓인 초보나 다름없는 자바 실력에 여기까지 가지가 치게 될 줄이야…

그저 난 Spring Data – JPA를 학습하고 싶었을 뿐인데, 검색을 통해 찾은 한 외국친구의 블로그에 있던 JPA 시리즈에서 서블릿 3.0 부터 지원되는 ServletInitialize를 통해서 web.xml의 양을 확 줄이는 걸 본 것이다. 사실.. 이해를 못해서 찾아보다 보니 여기까지 왔다고 해야 하나? 아무튼 지금 머리가 핑핑 돌 지경이다.

자세한 메커니즘이 있는데, 그건 여기를 확인해보면 되고, WebApplicationInitializer를 구현함으로써, web.xml의 상당부분을 자바 코드를 통해 할 수도 있고, 아예 없앨 수도 있다. 아무튼, 자바 진영 쪽이 그동안 xml의 홍수에서 어떻게든 벗어나려 하는 눈물겨운 노력을 볼 수 있다.

간단한 web.xml의 예제를 통해 어떻게 변경이 되는지 살펴보면…


  dispatcher
  org.springframework.web.servlet.DispatcherServlet
  
    contextConfigLocation
    /WEB-INF/spring/dispatcher-config.xml
  
  1



  dispatcher
  /main

web.xml이 이렇게 바뀐다.

public class MyWebAppInitializer implements WebApplicationInitializer {
  @Override
  public void onStartup(ServletContext container) {
    XmlWebApplicationContext appContext = new XmlWebApplicationContext()
    appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");

    ServletRegistration.Dynamic dispatcher =
      container.addServlet("dispatcher", new DispatcherServlet(appContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/main");
  }
}

WebApplicationInitializer의 구현체는 onStartup() 함수만 구현하면 된다. 사용가능한 함수들은 web.xml에서 사용했던 것과 크게 다르지는 않지만(패키지는 그대로 사용하면 되고, 파라미터들은 set으로 시작하는 카멜 케이스로 바뀌면 대충 맞다.), 매핑되는 함수들을 알고가야 할 듯 하다. 그건 나중에 찾아보고…

이렇게 구현하고 classpath에 두면 자동으로 SpringServletContainerInitializer가 감지해서 작동한다고 한다.

그러나, 위의 예제에서는 스프링에서 기본으로 사용하는 XMLWebApplcationContext를 통해 다른 스프링 빈 설정파일을 불러오고 있기 때문에 여전히 xml 설정에 많이 의존하게 된다. web.xml마저 자바빈 스타일로 구현하는데, 다른 설정역시 그렇게 해야 할 것 같지 않을까? 그래서, 외국친구들은 주로 AnnotationConfigWebApplicationContext를 이용해서 @Configuration 애노테이션을 이용하는 방식으로 구현한다.

public class MyWebAppInitializer implements WebApplicationInitializer {
  @Override
  public void onStartup(ServletContext container) {
    // Create the 'root' Spring application context
    AnnotationConfigWebApplicationContext rootContext = 
      new AnnotationConfigWebApplicationContext();
    rootContext.register(AppConfig.class);

    // Manage the lifecycle of the root application context
    container.addListener(new ContextLoaderListener(rootContext));

    // Create the dispatcher servlet's Spring application context
    AnnotationConfigWebApplicationContext dispatcherContext = 
      new AnnotationConfigWebApplicationContext();
    dispatcherContext.register(DispatcherConfig.class);

    // Register and map the dispatcher servlet
    ServletRegistration.Dynamic dispatcher = 
      container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/main");
  }
}

이렇게 web.xml을 이용하지 않고 자바빈 스타일을 이용함으로써 얻는 장점은 무엇일까?

가장 큰 이점은 서버가 구동되거나, 어플리케이션이 초기화될 때 뭔가 작업을 할 수 있다는 것이다(쉽게 말하면 어플리케이션의 Bootstrap 역할). 사실 저 WebApplicationInitializer의 구현체 모습은 Grails의 Bootstrap.groovy와 너무 닮아있다. 이후에 자바빈 스타일의 설정까지 하게 되면 더욱 가까워진다. 기존에 web.xml을 이용했을 때, 이런 부트스트랩 성의 작업을 어떻게 수행할 수 있었을까? 과연 가능하긴 했을까?

왜 서블릿 3.0을 사용해야 할 것인지에 대해서 다른 사람들을 설득할만한 근거가 부족하긴 하지만, Rails로 몇개월 간 개발해 본 경험 + Grails를 주의깊게 살펴본 짧은 지식을 근거로 부트스트랩 파일을 손쉽게 작성할 수 있다는 것은 매우 큰 이점이 될 수 있다.

참고 1) @Configuration 사용시 주의점

AnnotationConfigWebApplicationContext() 클래스를 이용해서 자바빈 스타일로 빈 설정을 할 경우 @Confuguration을 사용하게 되는데, 이 때 주의할 점이 있다. 룰루랄라 서버를 구동시켜보면 잔뜩 예외를 보게 될 테니…

이유는 CGLIB 가 필요하기 때문이다. 왜 CGLIB 패키지가 필요한지는 자세히 찾아봐야 할 듯 한데, 잠시 살펴본 결과로는 외부 리소스들을 설정시 불러올 때 패스의 문제인 듯 하다. Maven을 사용한다면 다음과 같이 추가만 해주면 에러는 나지 않는다.


  cglib
  cglib
  2.2.2

참고 2) web.xml과 병행해서 사용하기

WebApplicationInitializer를 구현하여 web.xml을 완전 없앨 수 있지만, XML 설정만 지원하는 패키지가 있을 수 있다. 이럴 경우는 web.xml과 병행해서 사용할 수 있는데, 이때 주의할 점이 있다. 바로 web.xml 내부에 있는 서블릿 버전에 대한 명시이다.

web.xml 의 에 version 속성이 있는데 Spring Tool Suite 같은 것으로 프로젝트를 생성하면 보통 2.5로 명시되어 있다. 이럴 경우 WebApplicationInitializer를 아무리 구현해놔도 web.xml만 응답을 하게 된다. 따라서 version 속성의 값을 3.0으로 변경해 주어야 한다.




  <!-- 기타 설정 들...-->


댓글 남기기