Web

Ch12. MVC 2 : Validator의 범위

tose33 2022. 7. 22. 16:56

스프링 MVC에는 모든 컨트롤러에 적용할 수 있는 글로벌 범위 Validator 단일 컨트롤러에 적용할 수 있는 Validator를 설정하는 방법이 있다. 

 

글로벌 범위 Validator (@Valid) 

글로벌 범위 Validator는 모든 컨트롤러에 적용할수 있는 Validator 이다.

글로벌 범위 Validator를 적용하려면 다음 두 가지를 설정해야 한다.

 

1. 설정 클래스에서 WebMvcConfigurer의 getValidator() 메서드가 Validator 구현 객체를 리턴하도록 구현 

 

package config;

// .. 

// Spring MVC 설정 
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer
{
	// .. 생략 
    
	// 스프링은 getValidator()가 리턴한 객체를 글로벌 범위 Validator로 사용 
	@Override 
	public Validator getValidator() 
	{
		return new RegisterRequestValidator();
	}
}

 

2. 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 애노테이션 적용 

package controller;

// .. 

@Controller
public class RegisterController 
{
	// .. 

	// 회원가입 form 데이터 받아서 처리하고 step3 (회원가입 완료 페이지) 이동 
	@PostMapping("/register/step3")
	public String handleStep3(@Valid RegisterRequest regReq, Errors errors)
	{
		// 에러 있다면 step2 로 되돌아감 
		// @Valid에서 처리 
//		new RegisterRequestValidator().validate(regReq, errors);
		if(errors.hasErrors()) return "register/step2";
		
        // .. 
	}	
}

커맨드 객체 ReigsterRequest 앞에 @Valid 애노테이션을 붙여 적용했다.

이렇게 되면 @Valid 애노테이션에 의해 Validator가 해당 타입을 검증할수 있는지 확인하고 (RegisterRequestValidator의 supports 메서드), 에러가 있는지 확인한다 (validate 메서드) .

 

위 과정은 요청 처리 메서드 (handleStep3) 실행 전에 수행된다. 

 


컨트롤러 범위 Validator (@InitBinder)

특정 컨트롤러를 검사할 Validator의 설정은 다음과 같이 할수 있다. 

 

package controller;
// ... 

@Controller
public class RegisterController 
{
	// ... 

	// 회원가입 form 데이터 받아서 처리하고 step3 (회원가입 완료 페이지) 이동 
	@PostMapping("/register/step3")
	public String handleStep3(@Valid RegisterRequest regReq, Errors errors)
	{
		// 에러 있다면 step2 로 되돌아감 
		// @Valid에서 처리 
//		new RegisterRequestValidator().validate(regReq, errors);
		if(errors.hasErrors()) return "register/step2";
		
		try 
		{
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch(DuplicateMemberException ex) 
		{
			errors.rejectValue("email", "duplicate");
			return "register/step2";
		}
			
	}
	
	// @Valid 가 붙은 커맨드 객체 검사할 Validator 설정 
	// 컨트롤러의 요청 처리 메서드 실행 전 매번 실행 
	@InitBinder
	protected void initBinder(WebDataBinder binder) 
	{
		binder.setValidator(new RegisterRequestValidator());
	} 
	

}

 

handleStep3 메서드의 파라미터에 커맨드 객체 RegisterRequest 앞에 @Valid 애노테이션을 붙였다.

그리고 @InitBinder가 붙은 initBinder 메서드에서 WebDataBinder#setValidator()로 RegisterRequestValidator를 등록하고 있다.

 

이것은  RegisterReqeust 객체를 검사할 Validator를 RegisterRequestValidator로 지정하겠다는 의미이다.

 

 

@InitBinder가 붙은 메서드는 컨트롤러의 요청 처리 메서드 (handleStep1, handleStep3 등 @Mapping이 붙은 메서드들)들이 실행되기 전 매번 실행되어 initBinder 메서드를 호출해 WebDataBinder를 초기화 한다. 

 

 

 

글로벌 범위, 컨트롤러 범위 Validator의 우선 순위 

만약 어떤 컨트롤러가 글로벌 범위와 컨트롤러 범위 두 개의 Validator가 검사하도록 되어 있고, WebDataBinder#setValidator()로 컨트롤러 범위 Validator를 지정했다면, 컨트롤러 범위 Validator로 검사하게 된다.

 

WebDataBinder는 내부적으로 Validator 목록을 갖고 있고 여기에는 글로벌 Validator도 포함되는데, WebDataBinder#setValidator()로 Validator를 추가할때 기존에 있던 Validator를 삭제하고 추가하기 때문이다. 

 

 

 

출처 : 스프링5 프로그래밍 입문 (최범균 저)