Spring MVC 고급 설정: JavaConfig를 활용한 서블릿 및 필터 구성

기존 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에서 처리되는 요청에만 적용되어야 한다면, AbstractAnnotationConfigDispatcherServletInitializergetServletFilters() 메서드를 오버라이드하여 등록할 수 있습니다. 이 방법을 사용하면 필터의 매핑 경로를 별도로 선언할 필요 없이, 반환되는 모든 필터가 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() };
    }
}

태그: Spring MVC JavaConfig Servlet filter WebApplicationInitializer

6월 4일 03:01에 게시됨