Study/Spring

[스프링 웹 MVC 설정] 01. @EnableWebMvc와 WebMvcConfigurer

going.yoon 2022. 3. 1. 15:50

스프링에서 기본적인 전략은 DispatcherServlet.properties에서 제공해주지만, 

개발자가 직접 스프링 MVC의 구성요소를 Bean으로 등록할 수 있다.

 

아래의 예시는 row-level에서 직접 Bean을 등록해주는 방식으로 실무에서 이렇게 사용하는 경우는 없다고 보면된다. 단지 원리를 익히자면 그런 것이다 라고 보고 넘어가면 되는 예시코드이다.

 

// #Controller코드
@Controller
public class HelloController {

    @Autowired
    HelloService helloService;

    /*
    * /hello/1?name=gayoung&age=25
    * @param id
    * @param name
    * @return
    * */
    @GetMapping("/hello/{id}")
    @ResponseBody
    public String hello(@PathVariable int id, @ModelAttribute User usr)
    {
        // #1. 만약 uri에서 path에 변수가 하나만 날라올 때는 @PathVariable을 사용
        // #2. parameter가 name=gayoung&age=25 이렇게 날라올 때 객체형식으로 받아오고 싶으면 @ModelAttribute로 한번에 받아오게끔 사용
        // #3. 본문에 있는 파라미터 값을 가져오고 싶을 땐 @ResponseBody를 사용
        return "Hello, " + helloService.getName();
    }

}


// WebConfig.java 파일

@Configuration
@ComponentScan
public class WebConfig {

    @Bean
    public HandlerMapping handlerMapping(){
        RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
        // #4. 아래 두줄처럼 setting해주는 값이 없다면 스프링의 기본 전략을 따라가게 된다.
        handlerMapping.setInterceptors(); // #5. 핸들러를 처리하기 전에 모든 Servlet filter처럼 처리 전후 로직을 설정해줄 수 있음.
        handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE); // #6. 핸들러가 여러개일 경우 핸들러 맵핑사이의 order를 설정
        return handlerMapping;
    }

    @Bean
    public HandlerAdapter handlerAdapter(){
        RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
        // #7. 아까 Controller에서 설정해주었던 것을 아래처럼 resolver를 직접 설정해줄 수 있다.
        // 파라미터를 넣어서 setting을 해주어야 하지만 skip
        //handlerAdapter.setArgumentResolvers(); 이런식으로 resolver를 설정해줄 수 있다.
        return handlerAdapter;
    }

    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

 

 

 

그리고 이러한 설정을 좀 더 편하게 하기 위해서 사용하는 애노테이션 기반의 설정방식이 있다. @EnableWebMvc 인터페이스를  사용하기 위해 config 파일에 해당 애노테이션을 붙여준다. 

 

이 EnableWebMvc 인터페이스는 아래의 코드처럼 DelegatingWebMvcConfiguration을 상속받고 있는데, 

DelegatingWebMvcConfiguration란 WebMvcConfigurationSupport의 서브클래스로써, WebMvcConfigurer 타입의 빈들을 탐지하고, 이들에게 스프링 설정들을 커스터마이징 할 권한을 위임한다.

// #1. DelegatingWebMvcConfiguration 를 상속받는 EnableWebMvc 인터페이스
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}



// #2. WebMvcConfigurationSupport를 확장한 DelegatingWebMvcConfiguration.class
//이 클래스는 WebMvcConfigurer타입의 빈들을 탐지하고, 이들에게 설정들을 커스터마이징할 권한을 위임한다. 
/**
 * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
 * to all beans of type {@link WebMvcConfigurer} allowing them to customize the
 * configuration provided by {@code WebMvcConfigurationSupport}. This is the
 * class actually imported by {@link EnableWebMvc @EnableWebMvc}.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 */
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}

 

단, EnableWebMvc인터페이스를 사용하기 위해선 WebApplication이 시작될 때, DispatcherServlet이 사용하는 context 안에 servletContext를 꼭 명시해주어야 한다. DelegatingWebMvcConfiguration 에서 ServletContext를 참조해서 빈 설정을 하기 때문.

 

// WebApplication.class
public class WebApplication implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setServletContext(servletContext);
        // #2. servletContext를 지정을 꼭 해줘야 한다.
        context.register(WebConfig.class);
        context.refresh();

        DispatcherServlet dispatcherServlet = new DispatcherServlet(context); 
        // #1. dispatcherservlet에서 사용하는 context 지정
        ServletRegistration.Dynamic app = servletContext.addServlet("app", dispatcherServlet);
        app.addMapping("/app/*");
    }
}

 

다시 돌아가서 EnableWebMvc 인터페이스는 DelegatingWebMvcConfiguration를 상속받고 있으며, 

DelegatingWebMvcConfiguration란 WebMvcConfigurationSupport의 서브클래스로써, WebMvcConfigurer 타입의 빈들을 탐지하고, 이들에게 스프링 설정들을 커스터마이징 할 권한을 위임한다고 했다.

 

이제 설정파일에서 @EnableWebMvc 애노테이션 설정과 함께 WebMvcConfigurer 를 상속받는 방식으로 설정을 간소화 해보자.

@Configuration
@ComponentScan
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/",".jsp");
    }
    
}

너무 쉬워서 눈물이 난다. servlet context를 dispatcherServlet에 설정해주고, @EnableWebMvc와 WebMvcConfigurer를 사용해서 필요한 함수를 Override 해주면 끝이다.

 

 

스프링 부트에서는 아래와 같은 방식으로 MVC 를 설정해준다.

1. application.properties : 스프링부트의 스프링 MVC 기본 설정

2. @Configuration + implements WebMvcConfigurer : 스프링부트의 스프링 MVC 기본 설정 + 추가 설정

3. @Configuration + implements WebMvcCOnfigurer + @EnableWebMvc : 스프링부트의 스프링 MVC 기본 설정없이 직접 구현

 

 

 

 

근데 설정은 우선 여기까지만 알아보고, 다시 돌아와서 공부하자. 지금은 활용(구현) 부분 먼저 공부해야 할 것 같다.