230214 Spring Boot Validation
서버측 유효성 검사(validation)
보안적인 측면에서 유효성 검사는 UI,서버에서 둘 다 수행되어야 한다.
스프링부트 뿐만 아니라 스프링에서도 적용 가능
부트 2.3버전 이후는 Spring Boot Starter Validation 라이브러리 필요 -maven repository
dependencies에
implementation group : 'org.springframework.boot', name: 'spring-boot-starter-validation'
- ★@NotNull : null을 제외한다.-String, Long, Integer등등 전부 검사 가능
- ★@NotBlank : null, 공백을 허용하지 않음-String만 적용가능
- @NotEmpty : null을 허용하지 않음-String, Map, Array에 검사 가능
- ★@Pattern : 정규표현식에 맞는 문자열을 정의할 수 있다.-String만 적용가능
- @Email : email형식이어야 한다.-공백을 통과
이외에도 여러개 존재
위 어노테이션은 VO(DTO)클래스의 멤버변수에 적용해서 사용한다.
import는 javax.validation패키지를 사용한다.
@NotBlank(message="이름은 필수 입니다")
private String name
컨트롤러에서는 데이터를 받을 때 @Valid와 Errors객체를 사용하여 유효성 검사를 진행한다.
@PostMapping("/~~경로")
public String viewFrom(@Valid ValidVO vo, Errors errors)
@Valid : 유효성검사 진행
문제가 생기면 Errors객체에 에러가 발생된 정보를 넘김. 데이터 바인딩
문제가 없으면 정상 동작.
※int타입
문자, 공백이면 에러. 정수는 null값을 가질 수가 없음. 공백도 불가. 기본값은 0
공백으로 넘어온 걸 0으로 치환할 때 내부적으로 parsing에러가 발생하는 것. 따라서 @NotNull어노테이션이 없어도 에러발생임.
@Patter은 regexp라는 속성이 있다. regexp="정규표현식"으로 사용
Errors객체
- hasErrors() : 바인딩된 에러가 있다면 true
- getFieldErrors() : 유효성 검사에 실패한 필드 목록확인. list형태로 반환
- getField() : 유효성 검사에 실패한 변수명확인
- getDefaultMessage() : 유효성 검사에 실패한 변수의 에러메시지 확인
- isBindingFailure() : 유효성 검사에 바인딩에 실패한 경우(유효성검사가 아닌 자바 내부 에러) true -가령, int인데 공백이 들어온 경우.
이외에도 여러가지가 있다.
int타입으로 변수를 선언하면
.getDefaultMessage가
Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'salary'; nested exception is java.lang.NumberFormatException: For input string: ""
로 나온다.
유효성검사에 실패해서 나온 메시지가 아님! java convert에러.
=>int가 아닌 Integer를 사용하면 wrapper타입이라 기본값이 null이다.
숫자, 실수형의 원시타입은 기본값이 0이라서 공백맵핑이 불가능하기에 wrapper타입으로 선언하는 편이 좋다. 필수는 아님.
ValidController.java
package com.simple.basic.controller;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.simple.basic.command.ValidVO;
@Controller
@RequestMapping("/valid")
public class ValidController {
@RequestMapping("/ex01")
public String ex01() {
return "valid/ex01";
}
//@Valid -유효성검사를 진행, Errors-유효성검사에 실패하면 에러객체로 바인딩
@PostMapping("/actionForm")
public String actionForm(@Valid ValidVO vo, Errors error, Model model) {
//System.out.println(error.getErrorCount());//에러의 갯수
if(error.hasErrors()) { //에러가 있다면 true, 에러가 없다면 false
List<FieldError> list=error.getFieldErrors(); //에러가 발생된 목록
for(FieldError err: list) {
//System.out.println(err.getField());//에러필드명
//System.out.println(err.getDefaultMessage());//에러메시지
if(err.isBindingFailure()) {//유효성 검사의 실패가 아니라, 자바 내부의 에러라면 true 반환
model.addAttribute("valid_"+err.getField(),"형식이 올바르지 않습니다");
}else {//유효성 검사에 실패한 목록
model.addAttribute("valid_"+err.getField(),err.getDefaultMessage());
}
}
model.addAttribute("vo",vo); //사용자가 작성한 값을 화면으로
return "valid/ex01";//원래 화면으로
}
return "valid/ex01_result";
}
}
ex01.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>서버측 유효성 검사</h3>
<form action="actionForm" method="post">
<!-- null인 경우 에러가 발생하기 때문에 null처리를 진행해야 합니다. -->
이름:<input type="text" name="name" th:value="${vo!=null?vo.name:''}"/>[[${valid_name}]]<br/>
급여:<input type="text" name="salary" th:value="${vo!=null?vo.salary:''}"/>[[${valid_salary}]]<br/>
전화번호:<input type="text" name="phone" th:value="${vo!=null?vo.phone:''}"/>[[${valid_phone}]]<br/>
이메일:<input type="text" name="email" th:value="${vo!=null?vo.email:''}"/>[[${valid_email}]]<br/>
<input type="submit" value="확인"/>
</form>
</body>
</html>
vo값이 없으면 처음 화면에 들어올 때 에러가 발생한다. 따라서 삼항연산자로 vo가 null인지를 판별해 아닐 경우 value에 공백을 주고, vo값이 있으면 해당 값을 value에 넣어 이전에 입력한 값이 남아있도록 한다.
ex01_result.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>결과화면</h3>
</body>
</html>
ValidVO.java
package com.simple.basic.command;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ValidVO {
/*
* @NotNull -null값만 허용하지 않음(wrapper의 Integer, Long, String 등등)
* @NotBlank -null값과 공백을 허용하지 않음(String에만 적용)
* @NotEmpty - null값을 허용하지 않음(Array, List 등에 적용)
* @Pattern -정규표현식에 맞는 문자열을 정의할 수 있음(String에만 적용)
*
* @Email -이메일형식검증(공백은 통과)
* @Min -최소값
* @Max -최대값
*
*/
@NotBlank(message="이름은 필수입니다")
private String name;
//숫자, 실수형의 원시타입은 기본값이 0이라서 공백맵핑이 불가능하기에 wrapper타입으로 선언하는 편이 좋습니다.
@NotNull(message="급여는 필수입니다")
private Integer salary;
@Pattern(regexp="[0-9]{3}-[0-9]{4}-[0-9]{4}",message="전화번호 양식은 000-0000-0000입니다")
private String phone;
@NotBlank(message="이메일은 필수입니다")
@Email(message="email형식이어야 합니다") //email형식이어야 합니다, 단 공백은 통과
private String email;
}
발생한 에러에 맞게 메시지가 vo->컨트롤러->화면을 통해 출력된다. 전부 조건을 만족해 에러가 발생하지 않으면 성공페이지로 이동한다.