데이터 Binding, Validator - Spring

Jiwon Park·2023년 3월 24일
0

Binding

form 에서 데이터를 전송할 때 문자열로 넘어가는 값을 스프링에서는 보통 dto 필드의 타입에 맞게 자동 변환을 해주지만(checkbox로 여러개 넘길때도 String[] 타입 이면 배열 형태로 바인딩이 된다.)
자동 변환이 안되는 경우가 있는데 그럴 때는 따로 변환을 해주면 가능하다.


ex)Date타입으로 변환

  1. PropertyEditor 방식(양방향 - String->타입, 타입->String)
    -> 단점 : 동시성 문제가 있어 싱글톤을 적용하지 않아 변환할 때마다 객체 생성
//controller 메소드 추가 
	@InitBinder
    public void initBinder(WebDataBinder binder){
    	SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Data.class, new CustomDateEditor(df,false);
    }
  1. Formatter 방식 (양방향 - String->타입, 타입->String)
	//dto 어노테이션 추가
    @DateTimeFormat(pattern="yyyy-MM-dd")
    private Date birth;
    
    //@NumberFormat(pattern="###,###") -> 숫자

ex)문자열을 지정 구분자로 스플릿해서 문자열 배열로 변환

  1. PropertyEditor 방식
	@InitBinder
    public void initBinder(WebDataBinder binder){
        binder.registerCustomEditor(String[].class,"필드이름(생략하면 전체)", new StringArrayPropertyEditor("구분자"));
    } 
    
  1. Converter 방식(stateless, 단방향 - 타입A - >타입B)
    -> 상태를 가지지 않음, 싱글톤으로 둥록 가능
   public class StringToStringArrayConverter implements Converter<String,String[]>{
     private String separator;

     public StringToArrayConverter(String separator) {
         this.separator = separator;
     }
        @Override
        public String[] convert(String source){
			return source.split(separator)
        } 
  
   }
   
   @InitBinder
   public void initBinder(WebDataBinder binder) {
       binder.addCustomFormatter(new StringToArrayConverter("구분자"));
   }

컨트롤러 메소드의 바인딩할 객체의 바로뒤의 위치에 BindingResult 객체를 추가하면
바인딩 실패시 에러페이지로 바로 안 넘어가고 컨트롤러에서 검증 결과를 가져와 에러 메시지를 처리하도록 한다.


*PropertyEditor 목록링크

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/propertyeditors/package-summary.html

Validator

자바스크립트로 입력값을 검증했다고 서버에서 검증작업을 생략하면 매우 위험해진다.
브라우저에서 자바스크립트가 동작하지 않게 할수도 있고, 강제로 폼을 조작하는 등 자바스크립트는 클라이언트 측에서 실행되기 때문에 사용자가 쉽게 조작할 수 있다. 사용자가 악의적인 목적으로 입력값을 조작하여 보낼 수 있기 때문에, 클라이언트 측에서 검증을 수행한 것만으로는 보안상의 문제가 발생할 수 있다.
따라서, 입력값을 검증하는 과정에서 클라이언트 측과 서버 측 모두에서 검증을 수행하는 것이 가장 안전한 방법이다.

public class UserValidator implements Validator {
	@Override
	public boolean supports(Class<?> clazz) {
//		return User.class.equals(clazz); // 검증하려는 객체가 User타입인지 확인
		return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
	}

	@Override
	public void validate(Object target, Errors errors) { 
		System.out.println("UserValidator.validate() is called");
		
        //if(!target instanceof User) return;
		User user = (User)target; // supports를 통과했으므로 바로 캐스팅하면 됨
		
		String id = user.getId();
		
//		if(id==null || "".equals(id.trim())) {
//			errors.rejectValue("id", "required");
//		}
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id",  "required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
		
		if(id==null || id.length() <  5 || id.length() > 12) {
			errors.rejectValue("id", "invalidLength", new String[]{"5", "12"}, null);
		}
	}
}

1.컨트롤러 메서드 내에서 수동 검증 - Validator를 직접 생성하고, validate()를 직접 호출

@Controller
public class UserController{
  @Autowired UserValidator userValidator;

  @RequestMapping("/user", method=RequestMethod.POST)
  public String userAdd(@ModelAttribute User user, BindingResult result){
    this.userValidator.validate(user, result);

    if(result.hasError()){
      // 오류 정보가 있을 시
    } else{
      // 오류 정보가 없을 시
    }
  }

2. @Valid를 이용한 자동 검증
https://mvnrepository.com/artifact/javax.validation/validation-api/2.0.1.Final
컨트롤러에서 직접 validate()를 호출하여 검증하던 방식과 달리, 바인딩 과정에서 자동으로 검증이 진행되도록 할 수 있다.(@Valid user user

@Controller
 
public class UserController{
  	@InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.setValidator(new UserValidator()); // UserValidator를 WebDataBinder의 validator로 등록
    }

    @RequestMapping("/user", method=RequestMethod.POST)
    public String userAdd(@Valid User user, BindingResult result){

      if(result.hasError()){
        // 오류 정보가 있을 시
      } else{
        // 오류 정보가 없을 시
      }
    }
 
    

3. global validator
-servlet-context.xml 빈 등록


<annotation-driven validator="userValidator"/>


***여러개일 경우 @valid뒤에 @Qualifier("userValidator") 추가해서 식별 가능**

global Validator와 local Validator를 동시에 적용하는 방법

  //binder.setValidator(new UserValidator()); //추가로 등록하는거기 때문에 set사용x
  binder.addValidator(new UserValidator()); 
  //List<Validator> validatorList = binder.getValidators(); 
  //System.out.println("validatorList="+validatorList); // 등록된 Validator 확인

발생한 에러 메시지 view에 표시 - MessageSource

Spring Form 태그 라이브러리 사용

1. 인코딩 설정 UTF-8인지 확인
2. src/main/resources/UntitledTextFile(error_message.properties) 파일 작성

required=필수 항목입니다.
required.user.pwd=사용자 비밀번호는 필수 항목입니다.
invalidLength.id=아이디의 길이는 {0}~{1}사이어야 합니다.

3.servelt-context.xml

<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
		<beans:property name="basenames">
			<beans:list>
				<beans:value>error_message</beans:value> <!-- /src/main/resources/error_message.properties -->
			</beans:list>
		</beans:property>
		<beans:property name="defaultEncoding" value="UTF-8"/>
	</beans:bean>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 
	
  <form:form modelAttribute="user">
    <div id="msg"><form:errors path="id"/></div> <!--path = "필드명"-->
  </form:form>

검증2(validation)-springboot <- 클릭

profile
안녕하세요

0개의 댓글