스프링 MVC2_ 글로벌 범위 Validator와 컨트롤러 범위 Validator

글로벌 범위는 모든 컨트롤러에 적용할 수있는 Validator

 

 

글로벌 범위 Validator

 

1. 설정 클래스에서 getValidator() 메서드가 Validator 구현 객체를 리턴하도록 설정

 

설정 클래스

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    
    @Override // 컨트롤러 기능을 대신 해줌 
	// 글로벌 범위 
	public Validator getValidator() { // 소스 => 오버라이드
		
		return new RegisterRequestValidator();
	}
    }

 Validator 구현 객체

package controller;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import spring.RegisterRequest;

public class RegisterRequestValidator implements Validator {
	
	private static final String emailRegExp = 
			"^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" +
					"[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
	private Pattern pattern;
	
	public RegisterRequestValidator() {
		pattern = Pattern.compile(emailRegExp);
	}
	
	@Override
	public boolean supports(Class<?> clazz) {
		     // 대상이 되는 커맨드 객체
		return RegisterRequest.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		RegisterRequest regReq = (RegisterRequest) target;
		if(regReq.getEmail() == null  || regReq.getEmail().trim().isEmpty()) {
			errors.rejectValue("email", "required");
		}else {
			Matcher matcher = pattern.matcher(regReq.getEmail());
			if(!matcher.matches()) {
				errors.rejectValue("email", "bad");
			}
		}
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");
		ValidationUtils.rejectIfEmpty(errors, "password", "required");
		ValidationUtils.rejectIfEmpty(errors, "confirmPassword", "required");
		if(!regReq.getPassword().isEmpty()) {
			if(!regReq.isPasswordEqualToConfirmPassword()) {
				errors.rejectValue("confirmPassword", "nomatch");
			}
		}
		
		
	}

}

 

 

2. 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 애노테이션 적용

Errors 타입 파라미터 넣을 것

	@PostMapping("/register/step3") // BindingResult = Errors 동일 
	public String handleStep3(@Valid RegisterRequest regReq, BindingResult errors) {
		// 컨트롤러 범위
        // RegisterRequestValidator() 생성안해도 됨
//		new RegisterRequestValidator().validate(regReq, errors);
		if(errors.hasErrors()) {
			return "register/step2";
		}
		
		try {
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch (DuplicateMemberException ex) {
			
			return "register/step2";
		}
	}

메이븐이면 pom.xml에 dependency 추가

		<dependency>
		<groupId>javax.validation</groupId>
		<artifactId>validation-api</artifactId>
		<version>2.0.1.Final</version>
		</dependency>

 

 

 

@InitBinder 애노테이션을 이용한 컨트롤러 범위 Validator

@Valid 애노테이션으로 범위 지정 

위에 글로벌 범위 지정 설정 클래스 메서드 지워도 됨

	@PostMapping("/register/step3") // BindingResult = Errors 동일 
	public String handleStep3(@Valid RegisterRequest regReq, BindingResult errors) {
		// 컨트롤러 범위 
		// RegisterRequestValidator() 생성안해도 됨
		if(errors.hasErrors()) {
			return "register/step2";
		}
		
		try {
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch (DuplicateMemberException ex) {
			
			return "register/step2";
		}
	}
	
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		binder.setValidator(new RegisterRequestValidator());
	}

 

 

 

Bean Validation을 이용한 값 검증 처리

이 애노테이션을 사용하면 Validator 작성 없이 애노테이션만으로 커맨드 객체의 값 검증을 처리할 수 있다.

 

1. Bean 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>6.0.7.Final</version>
		</dependency>

 

커맨드 객체에 @NotNull, @Digits 등의 애노테이션을 이용해서 검증 규칙 설정

 

package spring;

import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

public class RegisterRequest {
	
	@NotBlank
	@Email
	private String email;
	@Size(min = 6)
	private String password;
	@NotEmpty
	private String confirmPassword;
	@NotEmpty
	private String name;

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getConfirmPassword() {
		return confirmPassword;
	}

	public void setConfirmPassword(String confirmPassword) {
		this.confirmPassword = confirmPassword;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean isPasswordEqualToConfirmPassword() {
		return password.equals(confirmPassword);
	}
}

 

3. 글로벌 범위 @EnableWebMvc 설정

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

 

 

4. @Valid 애노테이션을 붙여서 글로벌 범위 Validator로 검증

	@PostMapping("/register/step3") // BindingResult = Errors 동일 
	public String handleStep3(@Valid RegisterRequest regReq, BindingResult errors) {
		// 컨트롤러 범위 

		if(errors.hasErrors()) {
			return "register/step2";
		}
		
		try {
			memberRegisterService.regist(regReq);
			return "register/step3";
		} catch (DuplicateMemberException ex) {
			
			return "register/step2";
		}
	}

 

JSP

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <h2><spring:message code="member.info" /></h2>
    <form:form action="step3" modelAttribute="registerRequest">
    <p>
        <label><spring:message code="email" />:<br>
        <form:input path="email" />
        <form:errors path="email" />
        </label>
    </p>
    <p>
        <label><spring:message code="name" />:<br>
        <form:input path="name" />
        <form:errors path="name" />
        </label>
    </p>
    <p>
        <label><spring:message code="password" />:<br>
        <form:password path="password" />
        <form:errors path="password" />
        </label>
    </p>
    <p>
        <label><spring:message code="password.confirm" />:<br>
        <form:password path="confirmPassword" />
        <form:errors path="confirmPassword" />
        </label>
    </p>
    <input type="submit" value="<spring:message code="register.btn" />">
    </form:form>

<%--
    <form action="step3" method="post">
    <p>
        <label>이메일:<br>
        <input type="text" name="email" id="email" value="${registerRequest.email}">
        </label>
    </p>
    <p>
        <label>이름:<br>
        <input type="text" name="name" id="name" value="${registerRequest.name}">
        </label>
    </p>
    <p>
        <label>비밀번호:<br>
        <input type="password" name="password" id="password">
        </label>
    </p>
    <p>
        <label>비밀번호 확인:<br>
        <input type="password" name="confirmPassword" id="confirmPassword">
        </label>
    </p>
    <input type="submit" value="가입 완료">
    </form>
 --%>
</body>
</html>

 

결과

오류 메시지는 메시지 파일 등록된 내용이 아니라 Bean Validation 프로버이더가 제공하는 기본 에러 메시지이다.

 

 

Bean Validation 2.0을 사용하면 추가 애노테이션을 더 사용할 수 있다.

		<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>6.0.7.Final</version>
		</dependency>

 

 

 

 

 

자바스크립트 에러 처리 

 

Controller

package controller;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;
import spring.Join;

@Controller
@RequestMapping("/join")
@Slf4j
public class JoinController {
	
	@GetMapping
	public String joinForm(Model model) {
		
		model.addAttribute("join", new Join());
		return"join/register";
	}
	
	@PostMapping
	public String joinExe(@ModelAttribute("join") @Valid Join join, Errors errors) {
		
		if(errors.hasErrors()) {
			
			return"join/register";
		}
		
		log.info("----------->" + join);
		
		return"complete";
	}
	
}

 

 

스프링 설정 ( Controller @Bean 등록 ) 

package config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import controller.JoinController;
import controller.Mycontroller;
import controller.OrderController;
import controller.RegisterController;
import spring.MemberRegisterService;

@Configuration
public class ControllerConfig {

	@Autowired
	private MemberRegisterService memberRegSvc;

	@Bean
	public RegisterController registerController() {
		RegisterController controller = new RegisterController();
		controller.setMemberRegisterService(memberRegSvc);
		return controller;
	}
	
	@Bean
	public Mycontroller mycontroller() {
		return new Mycontroller();
	}
	
	@Bean
	public JoinController joinController() {
		return new JoinController();
	}
	
	@Bean
	public OrderController orderController() {
		return new OrderController();
	}

}

 

 

JSP

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="ko">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<link rel="stylesheet" href="css/register.css">
	<title>회원 가입</title>
</head>
<body>
	<div id="container">
		<h1>회원 가입</h1>
		<form:form method="post" id="register" modelAttribute="join">
			<ul id="user-info">
				<li>
					<label for="userid" class="field">아이디</label>
					<form:input path="userid" placeholder="4~15자리의 영문과 숫자로 입력" required="true" />
					<form:errors path="userid" />
				</li>
				<li>
					<label for="email" class="field">이메일</label>
					<form:input type="email" path="email" required="true" />
					<form:errors path="email" />
				</li>
				<li>
					<label for="userpw1" class="field">비밀번호</label>
					<form:password path="userpw1" placeholder="8자리 이상" required="true" />
					<form:errors path="userpw1" />
				</li>
				<li>
					<label for="userpw2" class="field">비밀번호 확인</label>
					<form:password path="userpw2" placeholder="8자리 이상" required="true" />
					<form:errors path="userpw2" />
				</li>
				<li>
					<label class="field">메일링 수신</label>
					<label class="r"><form:radiobutton
					                      value="true" path="mailing" />예</label>
					<label class="r"><form:radiobutton 
					                     value="false" path="mailing" />아니오</label>
				</li>
			</ul>
			<ul id="buttons">
				<li>
					<button type="submit" class="btn btnBlack">가입하기</button>					
				</li>
				<li>
					<button type="reset" class="btn btnGray">취소</button>
				</li>
			</ul>
		</form:form>
	</div>
	
	<script src="js/register-result.js"></script>
</body>
</html>

 

CSS

#container{
	width:600px;
	margin:0 auto;
}
ul {
	list-style:none;
}
ul li {
	clear:both;
}
.field {
	float:left;
	width:100px;
	font-weight:bold;
	font-size:0.9em;
	line-height: 55px;
	text-align:right;
	margin-right:15px;
}
input[type="text"], input[type="password"], input[type="email"] {
	float:left;
	width:350px;
	height:35px;
	border:1px solid #aaa;
	border-radius:5px;
	padding:5px;
	margin:10px 0;
	float:left;	
}

.r {
	line-height:55px;
}

#buttons > li {
	display:inline-block;
}
button {
	width:250px;
	height:50px;
	margin-right:10px;
	border:1px solid #ccc;
	background:#eee;
	font-size:0.9em;
}

 

JS

		var userId = document.querySelector("#userid");  // ‘아이디’ 필드를 가져와 변수에 저장
		var pw1 = document.querySelector("#userpw1");  // ‘비밀번호’ 필드를 가져와 변수에 저장
		var pw2 = document.querySelector("#userpw2");  // ‘비밀번호 확인’ 필드를 가져와 변수에 저장

		userId.onchange = checkId;
		pw1.onchange = checkPw;
		pw2.onchange = comparePw;

		function checkId() {			
			if (userId.value.length < 4 || userId.value.length > 15) {  // userId 필드 내용의 길이가 4 이하이거나 15 이상일 경우 실행
				alert("4~15자리의 영문과 숫자를 사용하세요.");  // 오류 메시지 출력
				userId.select();    // 다시 입력할 수 있도록 userId 필드 선택
			}
		}

		function checkPw() {			
			if (pw1.value.length < 8) {
        alert("비밀번호는 8자리 이상이어야 합니다.");  // 오류 메시지 표시
        pw1.value = "";  // ‘비밀번호’ 필드 지움
				pw1.focus();  // 비밀번호를 다시 입력할 수 있게 포커싱
			}
		}

		function comparePw() {						
			if(pw1.value != pw2.value) {
				alert("암호가 다릅니다. 다시 입력하세요.");
				pw2.value = "";  // ‘비밀번호 확인’ 필드 지움
				pw2.focus();   // 비밀번호를 다시 입력할 수 있게 포커싱
			}
		}

 

 

 

 

Checkbox => List로 받아서 Pizza 추가 주문건 출력해보기

 

커맨드 객체 ( DTO )

package spring;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@AllArgsConstructor
@Builder
@ToString
@NoArgsConstructor
public class Pizza {
	private String addorder;
	private String total;
	private List<Pizza> plist;
	private List<String> selectedAddOns;
	
}

 

Controller

package controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;
import spring.Pizza;

@Controller
@RequestMapping("/order")
@Slf4j
public class OrderController {
	
	@GetMapping
	public String order(Model model) {
		model.addAttribute("plist", new Pizza());
		

		
		return "order/pizzaOrder";
	}
	
	@PostMapping
	public String result(Model model, @ModelAttribute("plist") Pizza pizza) {
	    List<String> selectedAddOns = new ArrayList<>();
	    
	    
	    if (pizza.getSelectedAddOns() != null) {
	        for (String addOn : pizza.getSelectedAddOns()) {
	            selectedAddOns.add(addOn);
	        }
	    }

	    pizza.setSelectedAddOns(selectedAddOns);
	    model.addAttribute("total", pizza.getTotal());

	    return "order/result";
	}

}

 

스프링 설정 ( Controller @Bean 등록 )

package config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import controller.JoinController;
import controller.Mycontroller;
import controller.OrderController;
import controller.RegisterController;
import spring.MemberRegisterService;

@Configuration
public class ControllerConfig {

	@Autowired
	private MemberRegisterService memberRegSvc;

	@Bean
	public RegisterController registerController() {
		RegisterController controller = new RegisterController();
		controller.setMemberRegisterService(memberRegSvc);
		return controller;
	}
	
	@Bean
	public Mycontroller mycontroller() {
		return new Mycontroller();
	}
	
	@Bean
	public JoinController joinController() {
		return new JoinController();
	}
	
	@Bean
	public OrderController orderController() {
		return new OrderController();
	}

}

 

CSS

#container {
  width:400px;
  margin:0 auto;
}
fieldset {
  margin-bottom:20px;
  border:1px solid #eee;
}
#total {
  border:none;
  font-size:16px;
  font-weight:bold;
}

 

JS

var price = 24000;

var sideMenu = document.querySelectorAll(".checkbx");
var total = document.querySelector("#total");
total.value = price+"원";

for(i=0; i<sideMenu.length; i++){
    sideMenu[i].onclick = function(){
        if(this.checked == true){
            price += parseInt(this.value);
        }
        else{
            price -= parseInt(this.value);
        }
        total.value = price+"원";
    }
}

 

주문 JSP

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="ko">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>연습문제 1</title>
	<link rel="stylesheet" href="css/sol-1.css">
</head>
<body>
	<div id="container">
    <h1>피자 주문</h1>
		<form:form method="post" modelAttribute="plist">
      <fieldset>
        <legend>사이즈</legend>
        <p>Large - 24000 원 </p>
      </fieldset>
      <fieldset>
        <legend>추가 주문 </legend>        
          <label><form:checkbox path="plist[0].addorder" class="checkbx" value="800" label="피클(800원)"/></label>
          <label><form:checkbox path="plist[1].addorder" class="checkbx" value="300" label="칠리 소스(300원)"/></label>
          <label><form:checkbox path="plist[2].addorder" class="checkbx" value="200" label="디핑 소스(200원)"/></label>
          <label><form:checkbox path="plist[3].addorder" class="checkbx" value="4800" label="치즈스틱(4개, 4800원)"/></label>
          <label><form:checkbox path="plist[4].addorder" class="checkbx" value="2400" label="콘 샐러드(2400원)"/></label>      
      </fieldset>
      <fieldset>
        <legend>합계</legend>
        <form:input path="total" class="price" readonly="true" />
        
      </fieldset>
      <input type="submit" />
		</form:form>	
	</div>

	<script src="js/sol-1.js"></script>
</body>
</html>

 

결과 JSP

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>주문 결과</title>
</head>
<body>
    <div>
        <h2>주문이 완료되었습니다!</h2>
        <p>총 주문 가격: ${total} </p>

        <h3>선택한 추가 주문 내역:</h3>
        <ul>
            <c:forEach items="${plist.plist}" var="addOn">
                <c:if test="${not empty addOn.addorder}">
                    <li>${addOn.addorder}</li>
                </c:if>
            </c:forEach>
        </ul>
    </div>
</body>
</html>

 

 

'프로젝트 기반 자바(JAVA) 응용 SW개발자 취업과정' 카테고리의 다른 글

2023-08-30 72일차  (0) 2023.08.30
2023-08-29 71일차  (0) 2023.08.30
2023-08-24 68일차  (0) 2023.08.24
2023-08-23 67일차  (0) 2023.08.23
2023-08-22 66일차  (0) 2023.08.23

+ Recent posts