Burninghering's Blog
article thumbnail

데이터의 검증

Validator에는, 맨 위처럼

메서드 2가지가 있다.

1. 매개변수로 들어온 클래스가 검증기로 검증 가능한지 알려주는 메서드

2. 실제로 검증하는 메서드 (첫번째 타겟이 검증할 객체, 에러가 검증하다 발생한 결과를 저장하는 것)

 

인터페이스를 구현해서,

UserValidator라는 클래스를 구현해보았다. (주석 확인)

 

컨트롤러에서 검증을 하기엔 코드가 지저분해져서,

왼쪽과 같이 검증하는 부분을 별도로 분리해놓았다.

 

분리하고 나면 노란색 칸과 같이 간단해진다.


자동 검증 하는 방법

아래 파란색 칸 코드와 같이

@InitBinder를 이용해서

검증기를 등록해준다.

 

검증하려는 객체에다가 @Valid 어노테이션만 붙여주면 된다.

 

binder.setValidator()라는 코드로 validator를 WebDataBinder에 등록한다.

 

@InitBinder는 컨트롤러 하나에서만 동작 가능하다.

모든 컨트롤러에서 동작 가능한 검증기를 만들려고 한다면?

GlobalValidator로 만들고 bean으로 등록한다.

모든 컨트롤러에서 @Valid 어노테이션을 통해 검증가능해진다.

 

글로벌 검증기와 로컬 검증기를 동시에 적용하는 방법은

addValidators로 등록해주면 된다.


 

실습

사람들이 회원가입하며 등록한 정보를 User로 받는데,

받기 전에 유효성검사를 실시했었다.

유효성검사를 컨트롤러에서 하지 않고, 따로 만들어주기 위해 주석처리를 한다.

주석 처리한 뒤

검증기 부분을 미리 만들어주었다.


검증기를 실제로 만들어보기!

package com.fastcampus.ch2;

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

public class UserValidator implements Validator { //Validator 인터페이스를 받았다

	@Override
	public boolean supports(Class<?> clazz) {
		return false;
	}

	@Override
	public void validate(Object target, Errors errors) {

	}

}

자동으로 위와 같이 클래스가 만들어진다.

 

package com.fastcampus.ch2;

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

public class UserValidator implements Validator { //Validator 인터페이스를 받았다

	@Override
	public boolean supports(Class<?> clazz) {
		return User.class.equals(clazz); //어떤 객체를 검증하려고 하는데, 그 클래스의 객체 clazz가 넘어오는데, 그 객체가 User클래스와 같으면?
	}

	@Override
	public void validate(Object target, Errors errors) { //검증하기
		//target 타입이 모든 객체를 검증할 수 있도록 형변환
		User user =(User)target;
	}

}

만들다가 갑자기 강사님의 코드를 가져왔다

package com.fastcampus.ch2;

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


	public class UserValidator implements Validator {
		@Override
		public boolean supports(Class<?> clazz) {
//			return User.class.equals(clazz); // 검증하려는 객체가 User타입인지 확인
			return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
		}

		@Override
		public void validate(Object target, Errors errors) { 
			System.out.println("LocalValidator.validate() is called");

			User user = (User)target;
			
			String id = user.getId();
			
	//		if(id==null || "".equals(id.trim())) {
	//			errors.rejectValue("id", "required");
	//		}
			
			//id가 null이거나 공백일 때 그런 경우에는 errors객체에 필드이름을 id라고 하고, 에러코드를 required라고 저장해라
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id",  "required"); 
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
			
			
			//id가 5자보다 작거나 12자보다 길때는 invalidLength라는 에러코드로 저장해라
			if(id==null || id.length() <  5 || id.length() > 12) {
				errors.rejectValue("id", "invalidLength");
			}
		}
	}

 

RegisterController.java

	@PostMapping("/register/save")
	public String save(User user,BindingResult result, Model m) throws Exception {
		
		System.out.println("result="+result);
		System.out.println("User="+user);
		
		UserValidator userValidator = new UserValidator(); //userValidator를 생성해서
		userValidator.validate(user, result); //BindingResult는 Errors의 자손 //검증을 한 뒤
		
		//User객체를 검증한 결과 에러가 있으면, registerForm을 이용해서 에러를 보여주어야 한다. 
		if(result.hasErrors()) { //검증 결과가 담긴 result에 에러가 있다면
			return "registerForm";
		}

 

방금과 같이,

Validator를 직접 생성하고 validator를 직접 호출하는 방식이 수동검증 방식이다.


자동 검증을 하려면,

수동 검증 방식을 주석처리한 뒤

@InitBinder 어노테이션이 붙은 메소드에 binder에다가 setValidator로 UserValidator를 생성해서 지정해주면 된다.

@Controller
public class RegisterController {
	
	@InitBinder
	public void toDate(WebDataBinder binder) {
		
		ConversionService conversionService = binder.getConversionService();
		System.out.println("conversionService="+conversionService);
		
//		SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
//		binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
		binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#"));
		
		binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
	}

 

검증할 객체의 매개변수를 선언한 곳에 @Valid 어노테이션을 붙여주면 된다.

@Valid는 스프링이 아닌 자바 표준 어노테이션이라

Maven repo에서 가져다써야한다.

pom.xml에 붙여준다

프로젝트를 업데이트해준다

Maven Dependencies에 새로 생겼다!

 

이제 RegisterController.java로 가면

@Valid를 import할 수 있다.

 

회원등록 화면에 id를 5자 이하로 치면,

위와 같이 Field error가 콘솔에 나온다.


Gloval Validator 만들기

package com.fastcampus.ch2;

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


	public class GlobalValidator implements Validator {
		@Override
		public boolean supports(Class<?> clazz) {
//			return User.class.equals(clazz); // 검증하려는 객체가 User타입인지 확인
			return User.class.isAssignableFrom(clazz); // clazz가 User 또는 그 자손인지 확인
		}

		@Override
		public void validate(Object target, Errors errors) { 
			System.out.println("GlobalValidator.validate() is called");

			User user = (User)target;
			
			String id = user.getId();
			
	//		if(id==null || "".equals(id.trim())) {
	//			errors.rejectValue("id", "required");
	//		}
			
			//id가 null이거나 공백일 때 그런 경우에는 errors객체에 필드이름을 id라고 하고, 에러코드를 required라고 저장해라
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id",  "required"); 
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
			
			
			//id가 5자보다 작거나 12자보다 길때는 invalidLength라는 에러코드로 저장해라
			if(id==null || id.length() <  5 || id.length() > 12) {
				errors.rejectValue("id", "invalidLength");
			}
		}
	}

 

@Controller
public class RegisterController {
	
	@InitBinder
	public void toDate(WebDataBinder binder) {
		
//		ConversionService conversionService = binder.getConversionService();
//		System.out.println("conversionService="+conversionService);
		
//		SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd");
//		binder.registerCustomEditor(Date.class, new CustomDateEditor(df,false));
		binder.registerCustomEditor(String[].class, new StringArrayPropertyEditor("#"));
		
//		binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
		
		binder.addValidators(new UserValidator()); //set으로 하면 local을 쓰게 되므로 add로 바꿔준다
		List<Validator> validatorList = binder.getValidators(); //등록된 검증기들 가져오기
		System.out.println("validatorList="+validatorList);
	}

 

servlet-content.xml에 등록하기

결과

validator가 두 개 등록되었다!


이제는 콘솔이 아니라, 

에러 메세지를 클라이언트에게 직접 보여준다

servlet-context.xml에 등록해주고,

아래 파란 상자처럼 확장자가 properties인 파일을 만들어주어야 한다.

 

않이 UTF-8 설정해줬는데도

properties 파일이 저장이 안된다

그래서 영어로 해줬다

그리고 sevlet-context.xml에 추가해준다

그러면 리소스번들메시지소스가 해당 파일을 읽어서

키값에 맞는 메시지를 보여준다.

 

검증 메시지를 jsp로 출력하려면

 

 

 

<%@ page contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.net.URLDecoder" %>

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" />
    <style>
        * { box-sizing:border-box; }

        form {
            width:400px;
            height:600px;
            display : flex;
            flex-direction: column;
            align-items:center;
            position : absolute;
            top:50%;
            left:50%;
            transform: translate(-50%, -50%) ;
            border: 1px solid rgb(89,117,196);
            border-radius: 10px;
        }

        .input-field {
            width: 300px;
            height: 40px;
            border : 1px solid rgb(89,117,196);
            border-radius:5px;
            padding: 0 10px;
            margin-bottom: 10px;
        }
        label {
            width:300px;
            height:30px;
            margin-top :4px;
        }

        button {
            background-color: rgb(89,117,196);
            color : white;
            width:300px;
            height:50px;
            font-size: 17px;
            border : none;
            border-radius: 5px;
            margin : 20px 0 30px 0;
        }

        .title {
            font-size : 50px;
            margin: 40px 0 30px 0;
        }

        .msg {
            height: 30px;
            text-align:center;
            font-size:16px;
            color:red;
            margin-bottom: 20px;
        }
        .sns-chk {
            margin-top : 5px; 
        }
    </style>
    <title>Register</title>
</head>
<body>
<!--   <form action="<c:url value="/register/save"/>" method="post" onsubmit="return formCheck(this)"> -->

<form:form modelAttribute="user">

    <div class="title">Register</div>
    
    <div id="msg" class="msg"><form:errors path="id"/> </div> 
    
    <label for="">아이디</label>
    <input class="input-field" type="text" name="id" placeholder="8~12자리의 영대소문자와 숫자 조합">
    <label for="">비밀번호</label>
    <input class="input-field" type="text" name="pwd" placeholder="8~12자리의 영대소문자와 숫자 조합">
    <label for="">이름</label>
    <input class="input-field" type="text" name="name" placeholder="홍길동">
    <label for="">이메일</label>
    <input class="input-field" type="text" name="email" placeholder="example@fastcampus.co.kr"> 
    <label for="">생일</label>
    <input class="input-field" type="text" name="birth" placeholder="2020/12/31">
    <label for="">취미</label>
    <input class="input-field" type="text" name="hobby">
    <div class="sns-chk">
        <label><input type="checkbox" name="sns" value="facebook"/>페이스북</label>
        <label><input type="checkbox" name="sns" value="kakaotalk"/>카카오톡</label>
        <label><input type="checkbox" name="sns" value="instagram"/>인스타그램</label>
    </div>
    <button>회원 가입</button>
   
   </form:form> 
   
   <script>
       function formCheck(frm) {
            var msg ='';

            if(frm.id.value.length<3) {
                setMessage('id의 길이는 3이상이어야 합니다.', frm.id);
                return false;
            }

           return true;
       }

       function setMessage(msg, element){
            document.getElementById("msg").innerHTML = `<i class="fa fa-exclamation-circle"> ${'${msg}'}</i>`;

            if(element) {
                element.select();
            }
       }
   </script>
</body>
</html>

 

GlobalValidator.java를 수정해주어야 제대로 나온다

package com.fastcampus.ch2;

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


	public class GlobalValidator implements Validator {
		@Override
		public boolean supports(Class<?> clazz) {
//			return User.class.equals(clazz); // �����Ϸ��� ��ü�� UserŸ������ Ȯ��
			return User.class.isAssignableFrom(clazz); // clazz�� User �Ǵ� �� �ڼ����� Ȯ��
		}

		@Override
		public void validate(Object target, Errors errors) { 
			System.out.println("GlobalValidator.validate() is called");

			User user = (User)target;
			
			String id = user.getId();
			
	//		if(id==null || "".equals(id.trim())) {
	//			errors.rejectValue("id", "required");
	//		}
			
			//id�� null�̰ų� ������ �� �׷� ��쿡�� errors��ü�� �ʵ��̸��� id��� �ϰ�, �����ڵ带 required��� �����ض�
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id",  "required"); 
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
			
			
			//id�� 5�ں��� �۰ų� 12�ں��� �涧�� invalidLength��� �����ڵ�� �����ض�
			if(id==null || id.length() <  5 || id.length() > 12) {
				errors.rejectValue("id", "invalidLength", new String[] {"","5","12"}, null);
			}
		}
	}
errors.rejectValue("id", "invalidLength", new String[] {"","5","12"}, null);

 

 


본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.

http://bit.ly/3Y34pE0

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

 

profile

Burninghering's Blog

@개발자 김혜린

안녕하세요! 반갑습니다.