제목을 원래 "WebApplicationInitializer을 이용한 컨텍스트 초기화"라고 쓰려고 했다가 바꿨다. 제목 낚시의 달인 영회에게 배운대로 제목에서 친근하지 않은 단어는 빼기로.

서블릿 3.0을 사용하면 기존에 web.xml을 이용해서 서블릿 컨텍스트를 초기화하던 작업을 여러 파일로 쪼갤 수도 있고, 자바 코드를 이용할 수도 있다. 요즘엔 스펙 문서 읽는 게 너무 지루해서 기피하다보니 나도 서블릿 3.0 스펙을 꼼꼼하게 읽어본 것은 아니지만, 대충 봐도 그런 내용이 나온다.

자바코드와 web.xml 파일을 같이 사용할 수도 있고, 아예 web.xml 없이 자바코드만으로 초기화 작업을 할 수도 있다. 어짜피 web.xml은 ServletContext 오브젝트 초기화하는 데 사용되던 메타정보니까 직접 ServletContext를 다루면 되는 것이겠지.

이때 사용되는게 javax.servlet.ServletContainerInitializer인데 좀 특이하다. META-INF/services/javax.servlet.ServletContainerInitializer 파일(단순 텍스트 파일이다) 안에 구현 클래스를 지정해야 하고, 이게 또 jar 파일내에 있어야 하는 것 같다. web.xml이라는 간단한 표준 설정 파일을 없앴기 때문에 웹 앱 내의 모든 클래스, 심지어 jar 내의 클래스도 다 뒤져야 하는 부담이 생긴다. 그거 말고도 스캐닝할게 많은데. 그래서 스캐닝의 부담을 줄이기 위해서 javax.servlet.ServletContainerInitializer(이게 파일 이름이다)를 쓴 꼼수가 참 눈물난다. web.xml 같은 XML 없이도 개발이 가능하다고 큰 소리치긴 해야 겠는데, 그래도 ServletContainerInitializer 위치는 정해진데 명시해줘야 할 것 같고, 그렇다고 initializer.xml을 만들라고 하자니 애개 XML 또 들어갔네 할 것 같고. 그래서 파일 이름을 javax.servlet.ServletContainerInitializer으로 결정. 그래 XML 없다. 인정.

아무튼 ServletContainerInitializer는 jar 안에 META-INF/.. 과 함께 있을 경우에 동작하는데, 얘를 구현한 클래스에 @HandlesTypes를 사용하면 @HandlesTypes 안에 지정한 타입의 클래스를 모조리 찾아서 onStartup()의 파라미터로 넣어준다.

public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> classes, ServletContext ctx) throws ServletException;
}

이 방식은 jar로 패키징된 프레임워크가 애플리케이션내에 특정 타입으로 작성된 클래스를 사용해서 서블릿이나 필터, 리스너 형태의 프레임워크 모듈에서 사용하는 것을 가능하게 해준다. 예를 들어, 필터를 사용하는 보안 프레임워크를 만들었다고 하자. 여기에 사용자가 보안과 관련된 설정을 SecurityConfig이라는 인터페이스를 구현한 클래스를 만들어서 그 안에 configSecurity() 메소드 에서 구현하게 만들도록 한다. 그리고 보안 프레임워크 jar안에 ServletContainerInitializer를 구현한 초기화 클래스 SecurityInitializer를 만들고, META-INF에 등록하고, 그리고 @HandlesTypes(SecurityConfig.class)를 붙여주면, 서블릿 3.0 컨테이너는 SecurityConfig 타입의 클래스를 모조리 찾아서 SecurityInitializer의 onStartup()에 넘겨준다. 그러면 여기서 SecurityConfig 클래스로 작성된 보안관련 설정을 configSecurity() 메소드를 실행해가며 수집한 뒤에 이를 초기 설정으로 해서 보안관련 Filter를 셋업하고, 얘를 최종적으로 ServletContext에 필터로 등록해주면 된다.

보이는 것과 다르게 사용하기는 어렵지 않다.

XML 파일 같은 중앙집중형 설정방식이 사라졌을 때 어떻게 접근해야 할지에 대한 나쁘지 않은 아이디어 같다.

아무튼 스프링도 이걸 이용한다. spring-web.jar에 ServletContainerInitializer를 구현한 클래스가 있다. 당연히 META-INF/…도. @HandlesTypes은 WebApplicationInitializer다. 스프링의 ServletContainerInitializer 구현 클래스인 SpringServletContainerInitializer는 직접 서블릿 컨텍스트에 뭔가를 등록하지 않는다. 대신 그 작업을 WebApplicationInitializer로 다 위임한다. SpringServletContainerInitializer는 @Order 정보를 가지고 실행 순위 조종해주는 정도.

이제 WebApplicationInitializer를 만들어서 여기서 원하는 대로 앱 컨텍스트 만들고 리스너, 서블릿 등에 등록하고, 지지고 볶고 하면 된다. @HandlesTypes은 설정정보 수집 내지는 실제 등록될 서블릿이나 필터가 소모할 대상정도고, 서블릿 컨텍스트 초기화는 ServletContainerInitializer 구현 클래스내에서 일어나는 것이 원래 서블릿 3.0의 의도라고 생각되는데, 스프링은 이를 개발자가 작성하는 초기화 클래스에서 직접 컨텍스트 초기화(주로 스프링 컨텍스트 등록)에 사용하도록 확작한 셈이다. 스프링의 SpringServletContainerInitializer 문서를 보면, 그래서 주절주절 이에 대해서 설명이 많은데. 눈이 침침해서 다 읽기가 싫으네.

아무튼 어제 밤에 살펴본 SpringServletContainerInitializer와 WebApplicationInitializer에 대한 소감은 이정도로 마무리. 서블릿 3.0을 본격적으로 쓰기 시작하게 되면 그때 제대로 살펴봐야지.

web.xml 없이 WebApplicationInitializer에서 스프링 컨텍스트 초기화&등록 하는 것과 관련된 자세한 설명은…

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha