Day 90

dokiru·2023년 6월 5일
0

학원

목록 보기
49/51

유효성 검사

  • 보내는 값이 유효한지에 대한 검사
  • 서버 쪽에서 유효성 검사를 할 수 있는 어노테이션들 有

Bean Validator

  • pom.xml에 의존성 주입
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
		<dependency>
		    <groupId>javax.validation</groupId>
		    <artifactId>validation-api</artifactId>
		    <version>${javax.validation-version}</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
		<dependency>
		    <groupId>org.hibernate.validator</groupId>
		    <artifactId>hibernate-validator</artifactId>
		    <version>${org.hibernate.validator-version}</version>
		</dependency>
  • 여러 어노테이션들

@AssertTrue	: true 
@AssertFalse	: false
	 
@Max(value)	: value 값이 최대! (value 값보다 크면 오류)
@Min			: value 값이 최소! (value 값보다 작으면 오류)
	 
@DecimalMax(value=, inclusive=true/false)	: value 값이 최대! true value<=, false value <
	 	ex) DecimalMax(value=100, inclusive=false) < 100
	 	ex) DecimalMax(value=100, inclusive=true) <= 100
	 
@DecimalMin(value=, inclusive=true/false)
@Null			: Null 이어야 한다. (값이 없어야 정상)
@NotNull		: Null이 아니어야 한다. (값이 있어야 정상)
	 
@Digits(integer=3, fraction=5)		: 숫자 자릿수!
	 	ex) integer 세자리수(정수형 3자리수) fraction(소수점자릿수 5자리)

@Size(min=5, max=10)	: 글자 자릿수 (5~10자리면 정상)
	 
@Pattern(regexp=정규식)	: 정규식이 맞아야 정상!
	 	ex) @Pattern(regexp="^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$/;");
@NotEmpty	: 값이 뭐라도 있어야 정상! (띄어쓰기 입력하며 인정)
@NotBlank	: 값이 뭐라도 있어야 정상! (띄어쓰기 제외하고 뭐라도 입력)
	 
@Positive	: 양수가 들어와야 정상 
@PositiveOrZero : 양수 + 0까지 인정
@Negative : 음수가 들어와야 정상
@NegativeOrZero : 음수 + 0까지 인정
@Email : 이메일 형식이 맞아야한다. (입력값 사이에 @가 있느냐)
	 	ex) ab@cd : 이메일로 인정

ex. input에 들어온 값의 유효성 검사 후, 형식에 맞지 않을 경우 오류 메세지를 화면에 띄워주는 경우

  • input value를 담을 DTO에 어노테이션을 사용해서 제약조건을 걸어줌
package com.app.dto;

import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class InputFormDto {
	
    // 길이 8~14
	@Size(min = 8, max = 14)
	String input1;
	
    // 값이 최대 1000
	@Max(1000)
	String input2;
	
    // 띄어쓰기 제외한 값이 뭐라도 입력 되어야함
	@NotBlank
	String input3;
	
	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getInput2() {
		return input2;
	}
	public void setInput2(String input2) {
		this.input2 = input2;
	}
	public String getInput3() {
		return input3;
	}
	public void setInput3(String input3) {
		this.input3 = input3;
	}	
}
  • 화면에 유효하지 않은 데이터 입력 후 post 날림
  • ValidatorController에서 들어온 값을 검증한 후, bindingResult를 받아옴
@PostMapping("/valid_form")
	public String valid_input(@Valid InputFormDto inputFormDto ,
									 BindingResult bindingResult,
									 Model model) {
		//@Valid 요청으로 부터 들어온 값을 inputFormDto 객체에 매핑 시키고
		//내부에 Valid 검증 유효성 기준으로 검증을 수행해달라 
		
		System.out.println(inputFormDto.getInput1()); // 1234
		System.out.println(inputFormDto.getInput2()); // 5000
		
		System.out.println(bindingResult); // org.springframework.validation.BeanPropertyBindingResult: 4 errors (..이하 생략)
        
		
		if(bindingResult.hasErrors()) { // 에러가 있을 경우 true
			System.out.println("잘못된게 있다.");
			
			for(ObjectError objErr : bindingResult.getAllErrors()) {
            						// ObjectError타입의 List 리턴받음
				System.out.println("---------------------------");
				System.out.println(objErr.getDefaultMessage());
				System.out.println(objErr.getObjectName());
				System.out.println(objErr.getCode());
				
				
				String[] codes = objErr.getCodes();
				System.out.println("---------codes------------");
				for(String str : codes) {
					System.out.println(str);
				}
				
				if(codes[0].equals("Size.inputFormDto.input1")) {
					System.out.println("얘는 input1 사이즈가 잘못됐다.");
				}
				
				if(codes[0].equals("Max.inputFormDto.input2")) {
					System.out.println("얘는 input2 맥스값 잘못됐다.");
				}
				
				if(codes[0].equals("NotBlank.inputFormDto.input3")) {
					System.out.println("얘는 input3 공백이면 안되는데...");
				}
				
		}
			
			model.addAttribute("isError", "true");
			
			return "valid_form";
		}		
		return "valid_end";
	}
  • bindingResult
    : 검증 오류가 발생할 경우 오류 내용을 보관하는 스프링 프레임워크에서 제공하는 객체
org.springframework.validation.BeanPropertyBindingResult: 4 errors
Field error in object 'inputFormDto' on field 'input3': rejected value []; codes [NotBlank.inputFormDto.input3,NotBlank.input3,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [inputFormDto.input3,input3]; arguments []; default message [input3]]; default message [공백일 수 없습니다]
Field error in object 'inputFormDto' on field 'input1': rejected value [1234]; codes [Size.inputFormDto.input1,Size.input1,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [inputFormDto.input1,input1]; arguments []; default message [input1],14,8]; default message [크기가 8에서 14 사이여야 합니다]
Field error in object 'inputFormDto' on field 'input2': rejected value [5000]; codes [Max.inputFormDto.input2,Max.input2,Max.java.lang.String,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [inputFormDto.input2,input2]; arguments []; default message [input2],1000]; default message [1000 이하여야 합니다]
  • bindingResult.hasErrors() : boolean값 리턴 (오류가 있는지 없는지)!

  • bindingResult.hasAllErrors() : ObjectError 타입의 List 리턴

    • 각 ObjectError들 안에 내장되어 있는 메소드로 오류 메세지 (getDefaultMessage), 오류가 발생한 객체 이름(getObjectName), 오류 코드 이름(getCode) 확인 가능
    • getCodes는 오류결과를 모두 가지고 와서 String 배열을 리턴해주고, getCode는 그 배열의 가장 마지막 값을 리턴

  • 오류 메세지를 불러올 화면
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
	<style type="text/css">
		.errMsg {
			color:red;
			font-style: italic;
		}
	</style>
</head>
<body>
	<h1>valid_view</h1>
	<form action="" method="post">
		<label>input1<input type="text" name="input1" value="${inputFormDto.input1}"></label><br/>
		<span class="errMsg">
          	<!-- name : 오류 검사를 해야하는 객체 이름 -->
			<spring:hasBindErrors name="inputFormDto">
              	<!-- input1에 대한 에러를 가지고 있으면 (boolean) -->
              	<!-- hasBindErrors 태그 안에 있어야 bindingResult 안에 있는 errors 값에 접근 가능 -->
				<c:if test="${errors.hasFieldErrors('input1') }">
                  	<!-- input3에 대한 에러를 가져와서 defaultMessage 출력 -->
					${errors.getFieldError('input1').defaultMessage}
                  	<br />
                  	<spring:message code="${errors.getFieldError('input1').codes[0]}" /> (spring message로 처리한 custom error message)
											<!-- Size.inputFormDto.input1 -->
            	</c:if>
			</spring:hasBindErrors>
		</span>
		<hr/>
		
		<label>input2<input type="text" name="input2" value="${inputFormDto.input2}"></label><br/>
		<span class="errMsg">
        	<!-- name : 오류 검사를 해야하는 객체 이름 -->
			<spring:hasBindErrors name="inputFormDto">
              	<!-- input2에 대한 에러를 가지고 있으면 (boolean) -->
              	<!-- hasBindErrors 태그 안에 있어야 bindingResult 안에 있는 errors 값에 접근 가능 -->
				<c:if test="${errors.hasFieldErrors('input2') }">
                  	<!-- input2에 대한 에러를 가져와서 defaultMessage 출력 -->
					${errors.getFieldError('input2').defaultMessage}
                  	<br />
					<spring:message code="${errors.getFieldError('input2').codes[0]}" />(spring message로 처리한 custom error message)
											<!-- Max.inputFormDto.input2 -->
				</c:if>
			</spring:hasBindErrors>
		</span>
		<hr/>
		<label>input3<input type="text" name="input3" value="${inputFormDto.input3}"></label><br/>
		<span class="errMsg">
        	<!-- name : 오류 검사를 해야하는 객체 이름 -->
			<spring:hasBindErrors name="inputFormDto">
              	<!-- input3에 대한 에러를 가지고 있으면 (boolean) -->
              	<!-- hasBindErrors 태그 안에 있어야 bindingResult 안에 있는 errors 값에 접근 가능 -->
				<c:if test="${errors.hasFieldErrors('input3') }">
                  	<!-- input3에 대한 에러를 가져와서 defaultMessage 출력 -->
					${errors.getFieldError('input3').defaultMessage}
                  	<br />
					<spring:message code="${errors.getFieldError('input3').codes[0]}" />(spring message로 처리한 custom error message)
											<!-- NotBlank.inputFormDto.input3 -->
				</c:if>
			</spring:hasBindErrors>
		</span>
		<hr/>
		<c:if test="${isError == true }">
			입력 내용을 확인해주세요.
		</c:if>
		<button type="submit">보내기</button>
	</form>
</body>
</html>
  • spring 자체에서 제공하는 hasBindErrors 태그 활용
  • message 태그를 사용, code 속성에 property키를 설정해서 custom error message를 출력

errors.properties 파일 내용 : 에러 코드와 키값을 맞춰줌

화면 오류메세지 출력 결과

+ spring 자체에서 제공하는 태그들 ( 출처 : [Spring] 29. 스프링 JSP 태그 라이브러리와 <spring:message> 태그|작성자 Do KY )

요소명설명
<spring:message>클라이언트의 Locale에 따라 다른 메시지를 출력한다. (국제화)
<spring:theme>사용자가 지정한 테마에 따라 다른 stylesheet를 적용한다.
<spring:argument>메시지의 플레이스홀더(인덱스 값)의 값을 지정한다. <spring:message> 또는 <spring:theme>의 arguments 속성 대신 사용할 수 있다.
<spring:hasBindErrors>입력값 검사 오류, 바인딩 오류의 발생 여부와 오류 정보를 출력한다.
<spring:bind>지정한 객체와 프로퍼티에 연결된 바인딩 정보(BindStatus)를 구한다.
<spring:nestedPath>바인딩 정보(BindingStatus)에 접근할 때 지정하는 경로에 루트 경로를 지정한다.
<spring:url>URL을 생성한다.
<spring:param>요청 파라미터와 URL템플릿의 경로 변수값을 지정한다.<spring:url>에서 사용한다.
<spring:htmlEscape>HTML Escape 여부에 대한 기본값을 설정한다.

validator 인터페이스

: spring 자체에서 제공하는 인터페이스

public class MemberDtoValidator implements Validator {

	@Override
    // 주어진 객체(clazz)에 대해 Validator가 지원 가능한가?
	public boolean supports(Class<?> clazz) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
    // 주어진 객체(target)에 대해서 유효성 체크를 수행하고, 유효성 에러 발생시 주어진 Errors객체에 관련 정보를 저장할 수 있음
	public void validate(Object target, Errors errors) {
		// TODO Auto-generated method stub
		
	}

}

(출처 : [java] 전자정부프레임워크 validation 사용법 )

errors 인터페이스가 제공하는 메소드

  1. 객체에 대한 에러코드 추가
메소드 형식설명
reject(String errorCode)전 객체에 대한 글로벌 에러 코드를 추가
reject(String errorCode, String defaultMessage)전 객체에 대한 글로벌 에러 코드를 추가하고, 에러코드에 대한 메세지가 존재하지 않을 경우 defaultMessage 사용
reject(String errorCode, Object[] errorArgs, String defaultMessage)전 객체에 대한 글로벌 에러 코드를 추가하, 메세지 인자로 errorArgs를 전달, 에러코드에 대한 메세지가 존재하지 않을 경우 defaultMessage 사용
  1. 필드에 대한 에러코드 추가
메소드 형식설명
rejectValue(String field, String errorCode)필드에 대한 에러 코드를 추가
rejectValue(String field, String errorCode, String defaultMessage)필드에 대한 에러 코드를 추가하고, 에러코드에 대한 메세지가 존재하지 않을 경우 defaultMessage 사용
rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage)필드에 대한 에러 코드를 추가, 메세지 인자로 errorArgs를 전달, 에러코드에 대한 메세지가 존재하지 않을 경우 defaultMessage 사용

ex. 로그인 시 아이디와 비밀번호의 길이의 유효성 검사를 해야하는 경우
1. memberDto

package com.app.dto;

public class MemberDto {
	private String id;
	private String pw;
	private String name;
	
	public MemberDto() {
	}
	
	public MemberDto(String id, String pw, String name) {
		this.id = id;
		this.pw = pw;
		this.name = name;
	}
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPw() {
		return pw;
	}
	public void setPw(String pw) {
		this.pw = pw;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "MemberDto [id=" + id + ", pw=" + pw + ", name=" + name + "]";
	}
}
  1. memberDto값을 검사할 MemberDtoValidator
package com.app.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.app.dto.MemberDto;

public class MemberDtoValidator implements Validator {

	@Override
	public boolean supports(Class<?> clazz) {
		// TODO Auto-generated method stub
		return MemberDto.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		// TODO Auto-generated method stub
		MemberDto memberDto = (MemberDto)target;
		
		// 아이디에 대한 유효성 검사
		if (memberDto.getId().length() < 2 || memberDto.getId().length() > 13) {
			// errors 객체에 에러 코드 추가
			errors.rejectValue("id", "id.sizeError", "아이디 길이를 확인해주세요");
		}
		
		// 비밀번호에 대한 유효성 검사
		if (memberDto.getPw().length() < 4 || memberDto.getPw().length() > 12) {
			// errors 객체에 에러 코드 추가
			errors.rejectValue("pw", "pw.sizeError", "비밀번호 길이를 확인해주세요");
            /*
            	bindingResult에서 getCodes 반복문 돌리면 아마 이런식으로 나올 것..
            	-----------codes-----------
				pw.sizeError.memberDto.pw << properties 파일에 프로퍼티 키로 추가해서 spring:message로 처리
				pw.sizeError.pw
				pw.sizeError.java.lang.String
				pw.sizeError
			*/
		}
	}

}
  1. 로그인을 처리하는 controller에서 validator를 불러와서 매개변수로 데이터가 담겨있는 memberDto와 bindingResult를 넣어준다
package com.app.controller;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import com.app.dto.MemberDto;
import com.app.validator.MemberDtoValidator;

@Controller
public class QuizLoginController {
	@GetMapping("/quiz_login")
	public String quizView() {
		return "login_form";
	}
	
	@PostMapping("/quiz_login")
	public String postQuizView(@ModelAttribute MemberDto memberDto, BindingResult bindingResult) {
		MemberDtoValidator mValidator = new MemberDtoValidator();
        
        // 수동으로 유효성 검증 수행!
		mValidator.validate(memberDto, bindingResult);
		
        // validator에서 에러가 발생하면 bindingResult에 담길 것
		if (bindingResult.hasErrors()) {
			return "login_form";
		}
		return "login_success";
	}
}

(출처 및 참고 : Spring form 유효성 검사 (Spring Validator) )

profile
안녕하세요!

0개의 댓글