스프링 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 |