Spring Boot

230214 Spring Boot Validation

주영재 2023. 2. 14. 13:49

서버측 유효성 검사(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->컨트롤러->화면을 통해 출력된다. 전부 조건을 만족해 에러가 발생하지 않으면 성공페이지로 이동한다.