기존 Spring MVC 웹 프로젝트 설정 시 JavaConfig 기반 환경에서는 기본적인 DispatcherServlet 및 ContextLoaderListener만으로 충분하다고 가정하는 경우가 많습니다. 그러나 DispatcherServlet에 대한 추가적인 설정을 적용해야 하거나, 애플리케이션에 커스텀 서블릿(Servlet)이나 필터(Filter)를 등록해야 하는 상황이 발생할 수 있습니다. 이러한 고급 요구사항을 충족하려면 표준적인 JavaConfig 설정 방식만으로는 부족할 수 있습니다.
이 문서에서는 DispatcherServlet 및 ContextLoaderListener 외에 추가 서블릿과 필터를 서블릿 컨테이너에 등록하고 구성하는 방법을 설명합니다. 각 서블릿과 필터의 구체적인 기능보다는 등록 및 설정 메커니즘에 초점을 맞춥니다.
DispatcherServlet 사용자 정의 설정
DispatcherServlet의 동작을 세밀하게 제어하려면 javax.servlet.ServletRegistration.Dynamic 인스턴스를 활용해야 합니다. 서블릿 컨테이너에 새로운 서블릿을 등록하면 이 Dynamic 인스턴스가 반환되며, 이를 통해 서블릿에 대한 추가 구성을 수행할 수 있습니다.
DispatcherServlet의 경우, Spring의 AbstractAnnotationConfigDispatcherServletInitializer가 서블릿 생성을 담당하고 컨테이너에 등록합니다. 따라서 우리는 customizeRegistration() 메서드를 오버라이드하여 등록된 Dynamic 인스턴스를 얻고, DispatcherServlet 자체에 대한 추가 설정을 적용할 수 있습니다.
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializerConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
// DispatcherServlet 사용자 정의 설정
@Override
protected void customizeRegistration(Dynamic registration) {
// DispatcherServlet 로드 시점 우선순위 설정
registration.setLoadOnStartup(1);
// 초기화 파라미터 설정
registration.setInitParameter("configName", "springMvcConfig");
// Servlet 3.0 기반 멀티파트(multipart) 지원 설정
// 파일 업로드 임시 저장 디렉토리를 "/app/uploads/temp"로 지정
registration.setMultipartConfig(new MultipartConfigElement("/app/uploads/temp", 1048576, 2097152, 0));
}
// 다른 필요한 메서드들도 구현해야 합니다 (예: getRootConfigClasses, getServletConfigClasses, getServletMappings)
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class }; // 예시: 웹 설정 클래스
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
다른 서블릿 등록
DispatcherServlet 외의 다른 서블릿을 등록하려면 org.springframework.web.WebApplicationInitializer 인터페이스를 구현하는 초기화 클래스를 정의해야 합니다. 이 인터페이스는 서블릿 컨테이너가 시작될 때 프로그래밍 방식으로 서블릿 컨텍스트를 구성할 수 있도록 하는 Servlet 3.0+ 표준의 핵심 요소입니다.
다음은 SimpleContentServlet이라는 서블릿 클래스를 서블릿 컨테이너에 등록하는 예시입니다. 서블릿 등록 시 반환되는 Dynamic 인스턴스를 사용하여 서블릿 매핑과 같은 추가 설정을 할 수 있습니다.
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
// 예시 서블릿 (실제 구현은 생략)
class SimpleContentServlet implements Servlet {
// Servlet 인터페이스 메서드 구현
@Override public void init(javax.servlet.ServletConfig config) throws ServletException {}
@Override public javax.servlet.ServletConfig getServletConfig() { return null; }
@Override public void service(javax.servlet.ServletRequest req, javax.servlet.ServletResponse res) throws ServletException, java.io.IOException {
res.setContentType("text/plain");
res.getWriter().write("Hello from Simple Content Servlet!");
}
@Override public String getServletInfo() { return null; }
@Override public void destroy() {}
}
public class CustomServletRegistration implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 서블릿 등록
Dynamic customServlet = servletContext.addServlet("simpleContentServlet", SimpleContentServlet.class);
// 서블릿 매핑 설정
customServlet.addMapping("/api/data/*");
}
}
DispatcherServlet의 동작 이해
기존 Spring MVC 프로젝트에서 AbstractAnnotationConfigDispatcherServletInitializer를 상속받는 모든 클래스는 자동으로 DispatcherServlet을 구성합니다. 이는 Spring MVC가 내부적으로 WebApplicationInitializer 인터페이스를 구현하고 있기 때문입니다. 아래 상속 관계를 통해 이를 확인할 수 있습니다.
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {}
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {}
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {}
다른 필터 등록
서블릿과 마찬가지로 WebApplicationInitializer 인터페이스를 구현하여 애플리케이션에 커스텀 필터를 등록할 수 있습니다. 다음은 AccessControlFilter라는 필터 클래스를 등록하는 예시입니다.
import javax.servlet.Filter;
import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
// 예시 필터 (실제 구현은 생략)
class AccessControlFilter implements Filter {
@Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {}
@Override public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.FilterChain chain) throws java.io.IOException, ServletException {
System.out.println("AccessControlFilter: 요청 처리 중...");
chain.doFilter(request, response);
}
@Override public void destroy() {}
}
public class SecurityFilterRegistration implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 필터 등록
Dynamic securityFilter = servletContext.addFilter("accessControlFilter", AccessControlFilter.class);
// 필터 매핑 경로 추가
securityFilter.addMappingForUrlPatterns(null, false, "/secure/pages/*");
}
}
DispatcherServlet에 필터 매핑
특정 필터가 DispatcherServlet에서 처리되는 요청에만 적용되어야 한다면, AbstractAnnotationConfigDispatcherServletInitializer의 getServletFilters() 메서드를 오버라이드하여 등록할 수 있습니다. 이 방법을 사용하면 필터의 매핑 경로를 별도로 선언할 필요 없이, 반환되는 모든 필터가 DispatcherServlet에 자동으로 매핑됩니다.
import javax.servlet.Filter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
// 예시 필터 (실제 구현은 생략)
class RequestLoggingFilter implements Filter {
@Override public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {}
@Override public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.FilterChain chain) throws java.io.IOException, ServletException {
System.out.println("RequestLoggingFilter: URL = " + ((javax.servlet.http.HttpServletRequest) request).getRequestURI());
chain.doFilter(request, response);
}
@Override public void destroy() {}
}
public class WebAppInitializerConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
// ... (이전 customizeRegistration 및 기타 필수 메서드 구현) ...
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// DispatcherServlet에 매핑될 필터 등록
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new RequestLoggingFilter() };
}
}