Clean Code that Works.


http://blog.goyello.com/2011/12/16/enhancements-spring-mvc31/

역시 취미 삼아 ..

Spring 3.1이 개발된지 수개월이 지난 지금 드디어 릴리즈 됬다. 이번 배포 버전은 캐쉬 추상화, 빈 프로파일, 간단한 컨테이너 설정 등 몇가지 재미난 기능을 포함하고 있다. 아무튼 이 블로그 포스팅에서 스프링 3.1에서 내가 생각하는 TOP 5 개선에 대해서 설명할 것이다.

1. Flash Attributes support
플래쉬 어트리뷰트들은 요청 사이의 

2. @Validated annotation with support for JSR-303 validation groups.

스프링 MVC bean validation은 JSR303 그룹 벨리데이션을 지원하는 새로운 @Validated annotation을  지원한다. 이 그룹은 주어진 객체에 대해 검증을 하고 제약의 하위 집합을 정의 한다. 각 제약 조건에 대한 선언은 그룹이나 그것이 속한 그룹의 목록을 정의할 수 있다.


public class User {
    @NotNull
    private String name;
 
    @NotNull(groups = { Simple.class })
    private String address;
 
    @NotNull(groups = { Simple.class, Extended.class })
    private String licenceType;
 
    @NotNull(groups = { Extended.class })
    private String creditCard;
}



public interface Simple {
 
}
 
public interface Extended {
 
}

새로운 @Validated annotation에 대한 사용은 아래 예제에 나와 있다.


@Controller
public class MyController {
 
    @RequestMapping(value = "/user/default", method = RequestMethod.GET)
    public String createDefaultUser(@Validated({ Default.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'name'
        }
        return null;
    }
 
    @RequestMapping(value = "/user/simple", method = RequestMethod.GET)
    public String createSimpleUser(@Validated({ Simple.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'licenceType' and 'address'
        }
        return null;
    }
 
    @RequestMapping(value = "/user/extended", method = RequestMethod.GET)
    public String createExtendedUser(@Validated({ Simple.class, Extended.class }) User user, BindingResult result) {
        if(result.hasErrors()) {
            // Validation on 'creditCard' and 'licenceType' and 'address'
        }
 
        return null;
    }
}

스프링 3.1에 추가된 이 그룹 벨리데이션은 정말 편리하다. 이 벨리데이션 그룹에 대해서 더 많은 자료는 download and read the JSR 303 specification.

3. Support @Valid on @RequestBody method arguments

@RequestMapping 핸들러 메서드는 HTTP request body에 접근할 수 있는 @RequestBody 어노테이션을 지원한다. 변환이 가능한 메시지는 메시지 컨버터를 통해 변환이 이루어진다. FormHttpMessageConverter(application/x-www-form-urlencoded), MarshallingHttpMessageConverter(application/xml), MappingJacksonHttpMessageConverter(appliction/json) 그리도 다른 converter들도. 하지만 @RequestBody 어노테이션이 붙은 변수들은 자동적으로 벨리데이션체크를 하지 않는다. 수동으로 체크해주는 로직이 필요.


@Controller
public class MyController {
 
    @Autowired
    private Validator validator;
 
    @RequestMapping(value = "", method = RequestMethod.PUT)
    @ResponseStatus(value = HttpStatus.CREATED)
    @Secured("ROLE_ADMIN")
    @Transactional
    public void save(@RequestBody User user) {
        validate(user);
        dao.save(user);
    }
 
    private void validate(User user) {
        // perfom validation using injected validator
    }
}

스프링 3.1에서 @RequestBody 메서드 어노테이션은 @Valid 메서드를 통해 자동으로 벨리데이션 체크를 할 수 있다. 이 방법은 코드를 심플하게 만들어 준다.

@Controller
public class MyController {
 
    @RequestMapping(value = "/user", method = RequestMethod.POST)
    @ResponseStatus(value = HttpStatus.CREATED)
    @Transactional
    public void save(@Valid @RequestBody User user) {
        dao.save(user);
    }
 
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public String handleMethodArgumentNotValidException(
            MethodArgumentNotValidException error) {
        return "Bad request: " + error.getMessage();
    }
}


위의 예제에서 스프링은 자동으로 유효성검사를 수행하고 에러가 발생할 경우 MethodArgumentNotValidException을 던져준다. 추가적으로 @ExceptionHandler 메서드는 이러한 유형의 에러 처리에 대한 사용자 지정 동작을 추가하여 만들 수 있다.

4. Supporting PUT request with form encoded data

서브릿 구현의 한계는 인코딩된 HTTP PUT 요청에 대한 처리를 하지 않는 다는 것이다. 새롭게 소개되는  HttpPutFormContentFilter는 이 문제를 해결 했다. 스프링 3.1에서 RESTfull or RESTLike API 는 좀 더 심플하게 만들 수 있다. 이것에 대한 샘플은 이전 포스팅에서 찾아볼수 있다.
previous post to read more about the usage of HttpPutFormContentFilter

5. Java based configuration

기존의 Spring MVC 애플리케이션의 XML 설정은 완벽하게 자바 기반의 설정으로 변경할 수 있다. 가장 간단한 웹 애플리케이션의 설정은 @EnableWebMvc 어노테이션을 사용하는 것이다.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" }, excludeFilters = @Filter(type = FilterType.ANNOTATION, value = Configuration.class))
@Import(PersistanceContextConfiguration.class)
public class ServletContextConfiguration extends WebMvcConfigurerAdapter {
 
    @Bean
    public TilesViewResolver configureTilesViewResolver() {
        return new TilesViewResolver();
    }
 
    @Bean
    public TilesConfigurer configureTilesConfigurer() {
        TilesConfigurer configurer = new TilesConfigurer();
        configurer.setDefinitions(new String[] { "/WEB-INF/tiles/tiles.xml",
                "/WEB-INF/views/**/views.xml" });
        return configurer;
    }
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("login");
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/").addResourceLocations(
                "/recourses/**");
    }
 
    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}


WebMvcConfigurerAdapter에 대한 확장은 필요하지 않지만 기존 스프링 MVC의 설정방법도 허락된다. 어댑터는 추가적인 컴포넌트(포맷터,컨버터, 커스텀 벨리데이터, 인터셉터등)을 등록할 수 있도록 한다. 이 사용자 정의 설정은 자바 코드만들 사용하여 가능하다.  configuring a Spring MVC application with no xml in a Servlet 3.0 

Spring MVC 3.1  has a lot to offer.
스프링 MVC 3.1은 많은 향상점을 제공한다. 이 블로그에서 언급한 기능은 스프링 MVC와 애플리케이션 개발에 긍정적인 영향을 끼칠 것이다. 혁명적이지는 않지만 스프링 3.1에서 릴리즈된 모든 확장기능과 기능 향상에 대해서 보면 프레임 워크가 올바른 방향으로 가고 있다고 느낄수 있다. 캐쉬 추상화, 빈 프로파일, 설정 간소화 등 많은 핵심 기능들은 더 효과적으로 애플리케이션 개발을 할 수 있도록 할 것이다.

오메 발번역. ..