Form 데이터 검증 [ Spring, Validator, @Valid, hibernate ]
반응형

 

아이디나 비밀번호 입력, 이메일 입력 등에서의 제약조건 (대,소문자,10글자 등)은 보통 js나 jquery로 Client에서 수행하는데, 이 작업을 서버(JAVA)단에서도 할 수 있는 것이다.

폼에서 전달되는 데이터를 위의 커맨드 객체(데이터 객체)에 담아 컨트롤 객체에 전달하는데, 이때 유효성 검사를 할 수 있다.

 

Validator 인터페이스

유효성 검사를 수행해주는 객체는 다음과 같다. 우선 Validator 인터페이스를 구현하는 클래스를 만든다.

validate는 유효성 검사, supports는 지원하는지에 대해 체크하는 메소드이다.

Error 객체의 rejectValue 메소드를 통해 에러정보를 추가할 수 있다.

public class MemberValidator implements Validator{

    public void validate(Object obj, Errors error) {
        System.out.println("validate action");
        Member member = (Member)obj;

        String id = member.getId();
        String pw = member.getPw();
        //id가 비었는지 확인
        if(id == null || pw.trim().isEmpty()) {
            error.rejectValue("id", "required");
        }
        //id가 20자 이상인지 확인
        else if(id.length() >= 10) {
            error.rejectValue("id", "long"); 
        }
        //pw 길이가 8자 이하인지 확인
        if(pw.length() <= 8) {
            error.rejectValue("pw", "short");
        }
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return Member.class.isAssignableFrom(clazz);
    }
}

이제 컨트롤러에서 다음 Valdiator를 활용한다.

@RequestMapping("/ex")
public String samplecontent(@ModelAttribute("mem") @Valid Member member, BindingResult result) { 

    MemberValidator validator = new MemberValidator();
    validator.validate(validator, result);

    String path = "sampleResult";
    if(result.hasErrors()) {
        path = "home";
    }
    return path;
}

BindingResult는 validate의 결과를 담는 객체로, hasErrors() 메소드를 통해 에러가 있는지 확인할 수 있다.

에러가 있으면 home (처음 id입력 뷰)로 돌아가게, 없으면 결과창으로 진행되게 하였다.

ValidationUtils 클래스를 사용하여 validate 메소드에서 조건을 일일히 안만들고 사용할 수도 있다.

pw가 비워져 있는지 확인하는 부분을 다음과 같이 한줄로 할 수 있다.

ValidationUtils.rejectIfEmptyOrWhitespace(error, "pw", "required");

 

@Valid, @InitBinder

직접 validate() 메소드를 호출하지 않고, 스프링에서 호출하는 방법이다.

일단 pom.xml에 의존을 추가한다.

<!-- Validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.8.Final</version>
</dependency>

그리고 위의 컨트롤러의 커맨드 객체 앞에 @Valid 키워드를 추가하여 스프링에서 자동으로 유효성 검사를 호출할 수 있도록 한다. (추가적으로 내가 Validator 객체를 생성하여 검사할 필요가 없다.)

@RequestMapping("/ex")
public String samplecontent(@Valid Member member, BindingResult result) { //Binding = 결과를 담아주는 객

    String path = "sampleResult";
    if(result.hasErrors()) {
        path = "home";
    }
    return path;
}

이때 미리 @InitBinder를 통해 Validator를 미리 설정해두어야 한다.

@InitBinder
protected void initBinder(WebDataBinder binder) {
    binder.setValidator(new MemberValidator());
}

이렇게 한다면, 자동으로 Spring에서 지정된 Validator (MemberValidator)를 실행해 유효성 검사를 수행한다.

 

hibernate 검사

hibernate에서 제공하던 검사 기능이 자바에서도 제공되면서 annotation으로 간편하게 검사할 수 있다.

간단한 annotation은 위의 hibernate-validator만으로 가능하나, DB 컬럼 매핑등 다양하게 사용하기 위해서는 hibernate-annotations를 사용해야 한다.

<!-- hibernate -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.5.6-Final</version>
</dependency>  
public class Member {

    @Id //DB 테이블에서 priamry key로 매핑
    @Size(min = 5, max=10, message="아이디는 5~10자로 작성해야 합니다.")
    String id;

    @Column
    @NotNull
    @Size(min = 8, max= 20, message="비밀번호는 8~20자로 작성해야 합니다.")
    @Pattern(regexp="^[0-9]+$", message="숫자만 입력가능합니다.")
    String pw;

    //getter, setter 설정

/* 아래 이외에도 다양한 종류가 존재한다.
@AssertTrue 참
@DecimalMax 지정 값 이하의 실수
@DecimalMin 지정 값 이상의 실수
@Digits(integer=,fraction=) 정수 여부
@Pattern(regexp="()") 원하는 패턴 여부
@Future 미래
@Past 과거
@Max 지정 값 이상
@Min 지정 값 이하
@NotNull
@Null 
@Size(min=,max=)  문자열 또는 배열등의 길이*/

이후 커맨드 객체에 @Valid를 적용하면 된다. @InitBinder는 하지 않아도 된다.

 

Validator 에러메시지 확인하기

hibernate를 사용한 검사에서 에러메시지를 확인하기 위해서 home.jsp를 수정해보자.

form 태그는 HTML form에 데이터 바인딩, 에러처리 등을 간편하게 할 수 있도록 도와준다.

다음과 같이 다양하게 존재한다.

<form:input> == <input type ="text">
<form:password> == <input type ="password">
<form:select>, <form:option>, <form:label>, <form:checkbox>

form 태그를 사용하기 위해서는 태그라이브러리를 import하고 사용하여야 한다.

modelAttribute는 Controller에서와 일치시켜야 하고 (다시 돌아왔을때 에러를 방지하기 위해)

에러는 errors 태그를 사용하여 에러정보를 출력할 수 있다.

path는 커맨드 객체의 특정 속성 (필드)로 이에 관련된 에러 메시지를 출력할 수 있게 해준다.

element는 에러메시지 출력시 사용할 HTML 태그, delimiter는 에러메시지 구분시 (여러개) 사용할 HTML 태그이다.

<!-- home.jsp -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<form:form action="/example/ex" method="post" modelAttribute ="mem">
    <form:input path="id"/>
    <form:errors path="id" element="div" delimiter=" "/>

    <form:password path="pw"/>
    <form:errors path="pw" element="div" delimiter=" "/>

    <button class="btn" type="submit"> 전송 </button>
</form:form>

 

근데 이대로만 한다면 HTTP 500 에러가 발생한다.

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'mem' available as request attribute

왜냐면 처음에 컨트롤러에서 -> 입력받는 뷰로 보낼때 mem이라는 타겟 객체가 없기 때문이다.

해결방법으로 빈 커맨더 객체를 입력받을 뷰를 호출할때 컨트롤러에서 보내주면 된다.

//빈 객체 추가해서 보내주기
model.addAttribute("mem", new Member());

return "home";

 

잘못 입력하면 다음과 같이 에러메시지가 표기됨을 확인할 수 있다.

 

 

반응형