티스토리 뷰

Chapter13. 유효성 검사 : 도서 등록 페이지의 오류 메시지 출력하기

 

 

 

 

13.1 유효성 검사의 개요

유효성 검사는 폼 페이지에서 입력 항목의 데이터 값이 서버로 전송되기 전에 정해진 규정으로 정확히 입력되었는지 계산 결과 등이 타당한지 검사하는 것이다.

예를 들어 숫자 검사, 아이디 중복 검사, 로그인 인증 시 아이디나 패스워드 검사, IP 패킷 검사 등이 있다.

 

 

 

13.1.1 유효성 검사의 유형

  • JSR-380(Java Bean Validation 2.0) 방식 : 웹 애플리케이션을 구성하는 특정 도메인 클래스의 멤버 변수, 즉 필드에 대한 유효성 검사 제약 사항(constraints) 애너테이션을 선언하여 해당 값이 올바른지 검증하는 방식
  • Validator 인터페이스의 구현체 방식 : 웹 애플리케이션을 구성하는 특정 도메인 클래스의 멤버 변수에는 제약 사항 애너테이션을 선언하지 않는다. 그 대신 스프링에서 제공하는 Validator 인터페이스로 구현하고 이를 Validator 인스턴스로 사용하여 해당 속성 값의 유효성 검사를 수행한다. 스프링 Validator 인터페이스는 애플리케이션의 모든 계층에서 유효성 검증을 위해 사용할 수 있다.

 

* 두가지 방법은 결과가 비슷한데 방식이 조금 다르다.

 

 

 유효성 검사를 해야하는 폼 데이터 항목

- 입력 데이터가 null인지에 대한 유효성 검사
- 날짜나 이메일을 입력할 떄 형식에 맞느지에 대한 유효성 검사
- 나이를 입력할 떄 숫자인지에 대한 유효성 검사
- 입력 데이터의 제한 길이를 초과했는지에 대한 유효성 검사
- 로그인 인증을 할 때 아이디와 패스워드에 대한 유효성 검사
- 회원 가입을 할 때 아이디 중복 여부에 대한 유효성 검사

입력 데이터의 유효성 검사 기능을 이용한다면 다양한 사용자가 작성하는 페이지임에도 폼 페이지는 동일한 양식과 형태를 유지할 수 있다.

 

 

 

13.1.2 @Valid를 이용한 유효성

스프링 MVC에서는 사용자가 폼 페이지에서 입력한 데이터의 유효성을 검사하기 위해 코드를 작성할 필요 없이 @Valid 애너테이션을 제공한다.

 

 

 

@Valid 사용하기 위한 환경설정

@Valid 는 스프링 MVC에서 사용자가 폼 페이지에서 입력한 데이터의 유효성 검사를 위해 코드를작성할 필요 없이 간단한 방법으로  @Valid 애너테이션을 제공한다.

 

 

 

step1. pom.xml 파일에 의존 라이브러리 등록하기

 

@Valid 를 이용해 폼 데이터 값에 대한 유효성 검사를 하고자하면 pom.xml 파일에 validation-api.jar과 hibernate-validator.jar 의존 라이브러리를 등록해야 한다.

  <!-- Validation-->
  <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>2.0.1.Final</version>
  </dependency>
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.4.2.Final</version>
  </dependency>

 

 

 

 

stpe2. 요청 처리 메서드의 매개변수에 @Valid 선언하기

 

@Vlid를 이용하면 컨트롤러 내 요청 처리메서드의 매개변수에 전달되는 폼데이터 값에 대한 유효성 검사를 실행할 수 있다.

@PostMapping("/...")
public String 메서드 이름(@Valid 매개변수, ..., BindinfReuslt result){
    if(result.hasErrors()){
    	//오류 메시지 저장
	}
	return "뷰 이름";
}

 

* @Valid 매개변수와 input 매개변수, form:errors path의 변수 이름은 모두  같아야 한다.

 

 

 

step3. 뷰 페이지에 오류 메시지 출력하기

 

유효성 검사를 해 발생한 오류 메시지를 jsp 뷰 페이지에 출력하려면 폼 태그 라이브러리인 <form:errors> 태그를 사용해야 한다.

<%@ taglib prefix="form" uri="http://www.springframework.org.tags" %>
...
<form:errors path="커맨드 객체의 멤버 변수 이름">

 

 

 

 


13.2 JSR-380으로 유효성 검사

JSR-380 애너테이션을 이용한 유효성 검사는 웹 애플리케이션을 구성하는 특정 도메인 클래스(DTO)의 프로퍼티(멤버 변수), 필드에 대해 유효성 검사의 제약 사항 애너테이션을 선언해 해당 멤버 변수 값이 오른지 검사하는 것이다.

이것은 Bena Validation 2.0이라고 부른다.

 

 

 

 

 

13.2.1 JSR-380 애너테이션 선언

유효성 검사가 필요한 도메인 클래스의 프로퍼티에 제약 사항을 설정하기 위해 JSR-380 애너테이션을 사용한다.

이때 애너테이션의 작성 위치는 DTO 변수의 위에 작성된다.

 

public class 클래스 이름{
    @제약 사항 애너테이션(속성[, message="오류 메시지 또는 [오류 코드]"])
    private String 멤버 변수;
    ...
    //Setter()와 Getter() 메서드 구현 생략
}

 

 

 

JSR-380 애너테이션 유형

유형 설명 속성
@AssertFalse 프로퍼티 값이 거짓인지 검사합니다.  
@AssertTrue 프로퍼티 값이 참인지 검사합니다.  
@DecimalMax 프로퍼티 값이 가질 수 있는 최대 실수 값을 검사한다. - value : 값
- inclusive : true/false
@DecimalMin 프로퍼티 값이 가질 수 있는 최소 실수 값을 검사한다. - value : 값
- inclusive : true/false
@Digits 프로퍼티가 가질 수 있는 지정된 범위(정수 부분의 자릿수와 소수 부분의 자릿수)를 검사한다. - integer : 정수의 자릿수
- fraction : 소수의 자릿수
@Future 프로퍼티 값이 미래 날짜(현재일 이후)인지 검사한다.  
@Max 프로퍼티 값이 가질 수 있는 최대 길이를 검사한다. value : 값
@Min 프로퍼티 값이 가질 수 있는 최소 길이를 검사한다. value : 값
@NotNull 프로퍼티 값이 Null이 아닌지 검사한다.  
@Null 프로퍼티 값이 Null인지 검사한다  
@Past 프로퍼티 값이 과거 날짜(현재일 이전)인지 검사한다  
@Pattern 프로퍼티 값이 정의된 정규 표현식에 일치하는 지 검사한다. regexp : 정규표현식
@Size 프로퍼티 값이 가질 수 있는 최대, 최소 길이를 검사한다. - min : 최소 길이
- max : 최대 길이
@Valid 객체에 대해 유효성 검사를 한다.  

 

 

 

JSR-380 의 기본 메시지 사용하기

유효성 검사 시 JSR-380이 선언된 클래스의 멤버 변수가 제약사항을 위반해 오류가 발생하면 hibernate-validator-xx.jar 라이브러리에서 제공하는 기본 메시지가 출력된다.

만약 기본 메시지가 아닌 사용자 정의 오류 메시지를 출력하려면 message 속성 값에 출력할 오류 메시지를 직접설정해야 한다.

 

 

JSR-380의 기본 메시지

애너테이션 기본 메시지
@AssertFalse 반드시 거짓(false)이어야 합니다.
@AssertTrue 반드시 참(true)이어야 합니다.
@DecimalMax 반드시  {value}보다 같거나 작아야 합니다.
@DecimalMin 반드시 {value}보다 같거나 커야 합니다.
@Digits 숫자 값이 허용 범위를 벗어납니다(허용 범위:<{integer} 자리>.<{fraction} 자리>).
@Future 반드시 미래 날짜이어야 합니다.
@Max 반드시 {value}보다 같거나 작아야 합니다.
@Min 반드시 {value}보다 같거나 커야 합니다.
@NotNull 반드시 값이 있어야 합니다.
@Null 반드시 값이 없어야 합니다.
@Past 반드시 과거 날짜이어야 합니다.
@Patern 정규 표현식 "{regexp}" 패턴과 일치해야 합니다.
@Size 반드시 최솟값 {min}과(와) 최댓값 {max} 사이의 크기이어야 합니다.

 

 

 

 

사용자 정의 오류 메시지 설정하기

기본 메시지가 아닌 사용자 정의 메시지를 출력하려면 message 속성을 사용해야 한다.

message 속성 값에 출력할 오류 메시지를 직접 정의하거나 메시지 리소스 파일(*.proeprties)을 만들어 '오류 코드=출력할 오류 메시지' 형식으로 정의해 message 속성 값에 오류 코드를 설정한다.

 

 

애너테이션.커맨드 객체 이름.필드 이름 = 출력할 오류 메시지

or

애너테이션 = 출력할 오류 메시지

 

 

@NotNull 애너테이션과 @Min 애너테이션에 오류 메시지 설정한 예시  (속성.클래스.key)

Notnull.Product.name = 값을 입력해주세요
Min.Product.price = 0 이상의 값을 입력해 주세요

 

 

 

13.2.2 @Valid 를 이용한 유효성 검사

 

	@PostMapping("/add")
	public String submitAddNewBook(@Valid @ModelAttribute("NewBook") Book book, BindingResult result, HttpServletRequest request) {
		
		if(result.hasErrors()) {
			return "addBook";
		}
		
		MultipartFile bookImage = book.getBookImage();

 

폼 페이지에서 입력된 값이 커맨드 객체 book으로 매핑될 때 유효성 검사를 진행한다. 이때 오류가 발생하면 오류 결과 값은 reulst 객체에 담기고 뷰 페이지인 addBooj으로 이동한다.

 

 

 

 

 

13.2.3 <form:errors> 태그로 오류 메시지 출력

유효성 검사를 할 때 발생한 오류 메시지를 jsp 뷰 페이지에 출력하려면 <form:errors> 태그를 사용해야한다.

 

<div class="col-sm-6">
    <form:errors path="unitPrice" cssClass="text-danger" />
</div>

 

unitPrice에 오류가 발생하면 message 속성에 설정된 사용자가 정의한 오류 메시지를 출력하고 설정되어 있지 않으면 기본 메시지를 출력한다.

 

 

 


13.2.4 JSR-380을 이용해 유효성 검사하기

 

pom.xml

      <!-- Validation-->
      <dependency>
		  <groupId>javax.validation</groupId>
		  <artifactId>validation-api</artifactId>
		  <version>2.0.1.Final</version>
	  </dependency>
	  <dependency>
		  <groupId>org.hibernate</groupId>
		  <artifactId>hibernate-validator</artifactId>
		  <version>5.4.2.Final</version>
	  </dependency>

 

messages.properties

Pattern =\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uB3C4\uC11CID\uC785\uB2C8\uB2E4(\uC22B\uC790\uB85C \uC870\uD569\uD558\uACE0 ISBN\uC73C\uB85C \uC2DC\uC791\uD558\uC138\uC694).
Size =\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uB3C4\uC11C\uBA85\uC785\uB2C8\uB2E4(\uCD5C\uC18C 4\uC790\uC5D0\uC11C \uCD5C\uB300 50\uC790\uAE4C\uC9C0 \uC785\uB825\uD558\uC138\uC694).
Min =\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC00\uACA9\uC785\uB2C8\uB2E4(0\uC774\uC0C1\uC758 \uC218\uB97C \uC785\uB825\uD558\uC138\uC694)
Digits =\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC00\uACA9\uC785\uB2C8\uB2E4(\uC18C\uC218\uC810 2\uC790\uB9AC\uAE4C\uC9C0, 8\uC790\uB9AC\uAE4C\uC9C0 \uC785\uB825\uD558\uC138\uC694)
NotNull = \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC00\uACA9\uC785\uB2C8\uB2E4(\uAC00\uACA9\uC744 \uC785\uB825\uD558\uC138\uC694).

 

 

Book.java

public class Book 
{
	@Pattern(regexp="ISBN[1-9]+")
	private String bookId;
	
	@Size(min=4, max=50)
	private String name;
	
	@Min(value=0)
	@Digits(integer=8, fraction=2)
	@NotNull
	private int unitPrice;

	.......
    
}

 

 

 

BookController.java

	@PostMapping("/add")
	public String submitAddNewBook(@Valid @ModelAttribute("NewBook") Book book, BindingResult result, HttpServletRequest request) {
		
		if(result.hasErrors()) {
			return "addBook";
		}
		
		MultipartFile bookImage = book.getBookImage();

 

 

addBook.jsp

 

에러가 생기면 출력하기 위해 <form:errors>를 추가적으로 입력해준다.

<div class="col-sm-6">
    <form:errors path="bookId" cssClass="text-danger" />
</div>

 

<div class="col-sm-6">
	<form:errors path="name" cssClass="text-danger" />
</div>

 

<div class="col-sm-6">
    <form:errors path="unitPrice" cssClass="text-danger" />
</div>

 

 

 

<body>
	<nav class="navbar navbar-expand navbar-dark bg-dark">
		<div class="container">
			<div class="navbar-header">
				<a class="navbar-brand" href="./home">Home</a>
			</div>
		</div>
	</nav>
	<div class="jumbotron">
		<div class="container">
		<!-- <h1 class="display-3">도서 등록</h1> -->
			<h1 class="display-3">
				<spring:message code="addBook.form.title.label" />
			</h1>
		</div>
	</div>
	
	<div class="container">
		<div class="float-right">
			<form:form action="${pageContext.request.contextPath }/logout" method="post">
				<input type="submit" class="btn btn-sm btn-success" value="Logout" />
			</form:form>
		</div>
		
		<div class="float-right" style="padding-right:30px">
			<a href="?language=ko">Korean</a>|<a href="?language=en">English</a>
		</div>
		
		<br><br>
		<form:form modelAttribute="NewBook" action="./add?${_csrf.parameterName }=${_csrf.token }" class="form-horizontal" enctype="multipart/form-data">
		<fieldset>
		   <%-- <legend>${addTitle}</legend> --%>
		<legend><spring:message code="addBook.form.subtitle.label" /></legend>
		
		<div class="form-group row">
			<label class="col-sm-2 control-label">
			<!-- <label class="col-sm-2 control-label">도서ID</label>    -->
				<spring:message code="addBook.form.bookId.label" />
			</label>
			<div class="col-sm-3">
				<form:input path="bookId" class="form-control" />
			</div>
			<div class="col-sm-6">
				<form:errors path="bookId" cssClass="text-danger" />
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2 control-label">
			 <!-- <label class="col-sm-2 control-label">도서명</label>    -->
				<spring:message code="addBook.form.name.label" />
			</label>
			<div class="col-sm-3">
				<form:input path="name" class="form-control" />
			</div>
			<div class="col-sm-6">
				<form:errors path="name" cssClass="text-danger" />
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2 control-label">
			   <!-- <label class="col-sm-2 control-label">가격</label>    -->
				<spring:message code="addBook.form.unitPrice.label" />
			</label>
			<div class="col-sm-3">
				<form:input path="unitPrice" class="form-control" />
			</div>
			<div class="col-sm-6">
				<form:errors path="unitPrice" cssClass="text-danger" />
			</div>
		</div>

 

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday