숨겨진 필드 요소
MemberController.java
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import kr.or.ddit.mapper.ComCodeMapper;
import kr.or.ddit.service.MemberService;
import kr.or.ddit.vo.Address;
import kr.or.ddit.vo.Card;
import kr.or.ddit.vo.ComCodeVO;
import kr.or.ddit.vo.Member;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
public class MemberController {
// DI(의존성 주입) / IoC(제어의 역전)
@Autowired
MemberService memberService;
@Autowired
ComCodeMapper comCodeMapper;
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member,
Model model) {
member.setCoin(1000);
//forwarding
return "registerForm05";
}
}
registerForm05.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<h2>Spring Form</h2>
<!-- modelAttribute 속성에 폼 객체의 속성명을 지정함 -->
<form:form modelAttribute="user" method="post" action="/registerForm01Post">
<p>
<!-- <input typd="hidden" id="coin" name="coin" /> -->
<!-- 위의 코드와 같음 -->
<form:hidden path="coin"/>
</p>
</form:form>
라벨 요소
registerForm05.jsp
라벨을 클릭 시 포커스가 감
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<h2>Spring Form</h2>
<!-- modelAttribute 속성에 폼 객체의 속성명을 지정함 -->
<form:form modelAttribute="user" method="post" action="/registerForm01Post">
<p><form:label path="userId">ID : </form:label>
<form:input path="userId" placeholder="아이디" />
</p>
<p><form:label path="userName">이름 : </form:label>
<form:input path="userName" placeholder="이름" />
</p>
<p><form:label path="password">패스워드 : </form:label>
<form:input path="password" placeholder="패스워드" />
</p>
</form:form>
입력값 검증
스프링 MVC는 Bean Validation 기능을 이용해 요청 파라미터 값이 바인딩된 도메인 클래스(또는 커맨드 클래스)의 입력값 검증을 함 (VO에서 입력값 검증을 하는 절차로 진행됨)
환경 설정이 꼭 필요함
- 환결 설정
pom.xml
</dependencies> 안에 추가
<!-- 입력값을 검증하기 위한 라이브러리 의존 관계 정의 시작
스프링
M(Model) : Service, ServiceImple, Mapper
V(View) : JSP
C(Controller) : Controller
Bean(자바빈 클래스, ArticleVO) Validation(유효성검사) 기능을 이용해
요청 파라미터 값이 바인딩된(멤버변수에 세팅된) 도메인 클래스(ArticleVO)의 입력값 검증을 함
요청 파라미터 : ?articleNo=112&title=개똥이
public String write(골뱅이ModelAttribute ArticleVO articleVO)
-->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.5.Final</version>
</dependency>
=> 추가한 후 run as에 maven build 해야 함
- Bean Validation이 제공하는 제약 애너테이션
NotNull | 빈 값 체크(int타입) |
NotBlank | null 체크, trim후 길이가 0인지 체크(String타입) |
Size | 글자 수 체크 |
이메일 주소 형식 체크 | |
Past | 오늘보다 과거 날짜(ex. 생일) |
Future | 미래 날짜 체크(ex. 예약일) |
AssertFalse | false 값만 통과 가능 |
AssertTrue | true 값만 통과 가능 |
DecimalMax(value=) | 지정된 값 이하의 실수만 통과 가능 |
DecimalMin(value=) | 지정된 값 이상의 실수만 통과 가능 |
Digits(integer=,fraction=) | 대상 수가 지정된 정수와 소수 자리수보다 적을 경우 통과 가능 |
Future | 대상 날짜가 현재보다 미래일 경우만 통과 가능 |
Past | 대상 날짜가 현재보다 과거일 경우만 통과 가능 |
Max(value) | 지정된 값보다 아래일 경우만 통과 가능 |
Min(value) | 지정된 값보다 이상일 경우만 통과 가능 |
NotNull | null 값이 아닐 경우만 통과 가능 |
Null | null일 겨우만 통과 가능 |
Pattern(regex=, flag=) | 해당 정규식을 만족할 경우만 통과 가능 |
Size(min=, max=) | 문자열 또는 배열이 지정된 값 사이일 경우 통과 가능 |
Valid | 대상 객체의 확인 조건을 만족할 경우 통과 가능 |
실습
Member.java
notNull인 컬럼을 VO에서 설정함
package kr.or.ddit.vo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
/*
1. 입력값 검증
스프링 MVC는 Bean Validation 기능을 이용해 요청 파라미터 값이 바인딩된
도메인 클래스(또는 커맨드 클래스)의 입력값 검증을 함
*/
/*
Bean Validation이 제공하는 제약 애너테이션
- NotNull : 빈 값 체크(int타입)
- NotBlank : null 체크, trim후 길이가 0인지 체크(String타입)
- Size : 글자 수 체크
- Email : 이메일 주소 형식 체크
- Past : 오늘보다 과거 날짜(ex. 생일)
- Future : 미래 날짜 체크(ex. 예약일)
- AssertFalse : false 값만 통과 가능
- AssertTrue : true 값만 통과 가능
- DecimalMax(value=) : 지정된 값 이하의 실수만 통과 가능
- DecimalMin(value=) : 지정된 값 이상의 실수만 통과 가능
- Digits(integer=,fraction=) : 대상 수가 지정된 정수와 소수 자리수보다 적을 경우 통과 가능
- Future : 대상 날짜가 현재보다 미래일 경우만 통과 가능
- Past : 대상 날짜가 현재보다 과거일 경우만 통과 가능
- Max(value) : 지정된 값보다 아래일 경우만 통과 가능
- Min(value) : 지정된 값보다 이상일 경우만 통과 가능
- NotNull : null 값이 아닐 경우만 통과 가능
- Null : null일 겨우만 통과 가능
- Pattern(regex=, flag=) : 해당 정규식을 만족할 경우만 통과 가능
- Size(min=, max=) : 문자열 또는 배열이 지정된 값 사이일 경우 통과 가능
- Valid : 대상 객체의 확인 조건을 만족할 경우 통과 가능
*/
//POJO가 약해짐
@Data
public class Member {
//입력값 검증 규칙을 지정함
@NotBlank
private String userId;
// 여러 개의 입력값 검증 규칙을 지정할 수 있음
@NotBlank
@Size(max=3)
private String userName;
private String password = "1234";
@DateTimeFormat(pattern="yyyy-MM-dd") private Date regDate;
private int coin;
//스프링폼에서 radiobuttons의 path로 사용
private String gender;
//스프링폼에서 radiobuttons의 item로 사용
private Map<String,String> genderCodeMap;
private String email;
private String birthDay;
//스프링폼에서 select의 path로 사용
private String nationality;
//스프링폼에서 select의 item로 사용
private Map<String,String> notionalityCodeMap;
private String cars;
private String[] carArray;
//스프링폼에서 checkboxes의 path로 사용
private ArrayList<String> carList;
//스프링폼에서 checkboxes의 item으로 사용
private Map<String, String> carMap;
private String hobby;
private String[] hobbyArray;
//스프링폼에서 checkboxes의 path으로 사용
private ArrayList<String> hobbyList;
//스프링폼에서 checkboxes의 item으로 사용
private Map<String,String> hobbyMap;
private String developer;
private boolean foreigner;
private String introduction;
private Date dateOfBirth;
// 중첩된(nested) 자바빈즈
// MEMBER : ADDRESS = 1 : 1
private Address address;
// MEMBER : CARD = 1 : N
private List<Card> cardList;
//스프링프레임워크에서 제공하는 MultipartFile 파일객체타입
private MultipartFile picture;
//<input type="file" name="pictures" multiple>
// 위의 input과 name 값이 변수명과 같아야 함
private MultipartFile[] pictures;
}
MemberController.java
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import kr.or.ddit.mapper.ComCodeMapper;
import kr.or.ddit.service.MemberService;
import kr.or.ddit.vo.Address;
import kr.or.ddit.vo.Card;
import kr.or.ddit.vo.ComCodeVO;
import kr.or.ddit.vo.Member;
import lombok.extern.slf4j.Slf4j;
import oracle.jdbc.proxy.annotation.Post;
@Slf4j
@Controller
public class MemberController {
// DI(의존성 주입) / IoC(제어의 역전)
@Autowired
MemberService memberService;
@Autowired
ComCodeMapper comCodeMapper;
//1) 컨트롤러 메서드의 매개변수로 자바빈즈 객체가 전달이 되면 기본적으로 다시 화면으로 전달
//컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는 다시 화면으로 폼 객체를 전달함
//2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
//3) ModelAttribute 애너테이션으로 폼 객체의 속성명을 직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member,
Model model) {
// model.addAttribute("member", new Member());
//폼 객체의 프로퍼티에 값을 지정
// member.setUserId("gaeddongi");
// member.setUserName("개똥이");
member.setCoin(1000);
member.setPassword("java");
member.setIntroduction("저는 개똥이 입니다.\n왜일까요?");
//모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면에 전달
Map<String, String> hobbyMap = new HashMap<String, String>();
hobbyMap.put("kmj", "Music");
hobbyMap.put("ksy", "FootBall");
hobbyMap.put("esb", "showping");
member.setHobbyMap(hobbyMap);
//Music을 미리 체크 처리
String[] hobbyArray = {"ksy"};
member.setHobbyArray(hobbyArray);
member.setHobby("ksy");
//보유자동차
Map<String,String> carMap = new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
//volvo을 미리 체크 처리
String[] carArray = {"volvo"};
member.setCarArray(carArray);
//성별
Map<String,String> genderCodeMap = new HashMap<String, String>();
genderCodeMap.put("Male", "남성");
genderCodeMap.put("Female", "여성");
genderCodeMap.put("etc", "기타");
member.setGenderCodeMap(genderCodeMap);
//미리 선택 처리
member.setGender("Female");
//11. 셀렉트 박스 요소
//국적 선택
Map<String, String> notionalityCodeMap =
new HashMap<String, String>();
notionalityCodeMap.put("Korea", "대한민국");
notionalityCodeMap.put("Germany", "독일");
notionalityCodeMap.put("Australia", "오스트레일리아");
member.setNotionalityCodeMap(notionalityCodeMap);
//국적 미리 선택
member.setNationality("Korea");
//국적을 공통코드로부터 가져와보자
ComCodeVO comCodeVO = this.comCodeMapper.getComCode("natn");
log.info("registerFOrm05->comCodeVO : " + comCodeVO);
model.addAttribute("comCodeVO", comCodeVO);
//forwarding
return "registerForm05";
}
/*
입력값 검증을 하기 위해서는 메서드 매개변수에 도메인 클래스를 정의하고 골뱅이Validated를 지정
입력값 검증 대상의 도메인 클래스 직후에 BindingResult를 정의함
BindingResult에는 요청 데이터의 바인딩 오류와 입력값 검증 오류 정보가 저장됨
*/
@PostMapping("/registerForm01Post")
public String registerForm01Post(
@Validated @ModelAttribute("user") Member member,
BindingResult brResult,
Model model
) {
log.info("registerForm01Post->member : " + member);
//1)true : 문제 있음 / 2)false : 문제 없음
log.info("brResult.hasErrors() : " + brResult.hasErrors());
/*----*/
//폼 객체의 프로퍼티에 값을 지정
member.setCoin(1000);
member.setPassword("java");
member.setIntroduction("저는 개똥이 입니다.\n왜일까요?");
//모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면(jsp)에 전달
Map<String,String> hobbyMap =
new HashMap<String, String>();
hobbyMap.put("Music", "음악");
hobbyMap.put("FootBall", "축구");
hobbyMap.put("Book", "도서");
hobbyMap.put("Travel", "여행");
member.setHobbyMap(hobbyMap);
//Music을 미리 체크 처리
String[] hobbyArray = {"kmj"};
member.setHobbyArray(hobbyArray);
member.setHobby("kmj");
//보유자동차
Map<String,String> carMap =
new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
//volvo를 미리 체크 처리
String[] carArray = {"volvo"};
member.setCarArray(carArray);
//성별
Map<String,String> genderCodeMap =
new HashMap<String, String>();
genderCodeMap.put("Male", "남성");
genderCodeMap.put("Female", "여성");
genderCodeMap.put("etc", "기타");
member.setGenderCodeMap(genderCodeMap);
//미리 선택 처리
member.setGender("Female");
//11. 셀렉트 박스 요소
//국적 선택
Map<String,String> notionalityCodeMap =
new HashMap<String, String>();
notionalityCodeMap.put("Korea", "대한민국");
notionalityCodeMap.put("Germany", "독일");
notionalityCodeMap.put("Australia", "오스트레일리아");
notionalityCodeMap.put("Canada", "캐나다");
member.setNotionalityCodeMap(notionalityCodeMap);
//국적 미리 선택
member.setNationality("Korea");
//국적을 공통코드로부터 가져와보자
ComCodeVO comCodeVO = this.comCodeMapper.getComCode("natn");
log.info("registerForm05->comCodeVO : " + comCodeVO);
model.addAttribute("comCodeVO", comCodeVO);
/*----*/
return "registerForm05";
}
}
registerForm05.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<h2>Spring Form</h2>
<!-- modelAttribute 속성에 폼 객체의 속성명을 지정함 -->
<form:form modelAttribute="user" method="post" action="/registerForm01Post">
<hr/>
<p><form:label path="userId">ID : </form:label>
<form:input path="userId" placeholder="아이디" />
</p>
<p><form:label path="userName">이름 : </form:label>
<form:input path="userName" placeholder="이름" />
</p>
<p><form:label path="password">패스워드 : </form:label>
<form:input path="password" placeholder="패스워드" />
</p>
<!-- 12. 숨겨진 필드 요소
HTML 숨겨진 필드를 출력하려면 hidden 요소를 사용
-->
<p>
<!-- <input typd="hidden" id="coin" name="coin" /> -->
<!-- 위의 코드와 같음 -->
<form:hidden path="coin"/>
</p>
<p>패스워드 :
<input type="password" name="password" id="password"/>
<!-- 값을 설정해서 뷰에 전달하더라도 패스워드 필드에 반영 안 됨 -->
<form:input path="password" /> <!-- 바인딩되어서 값이 들어옴 -->
</p>
<p>소개글 :
<form:textarea path="introduction" rows="6" cols="30"/>
</p>
<p>개발자여부 : (String)
<form:checkbox path="developer" value="Y" label="Y/없음"/>
</p>
<p>외국인여부 : (boolean)
<form:checkbox path="foreigner" value="false" label="trud/false"/>
</p>
<p>성별 : (String gender)
<form:radiobuttons items="${user.genderCodeMap}" path="gender"/>
</p>
<p>
<form:radiobutton path="gender" value="Male" label="남성"/>
<form:radiobutton path="gender" value="Female" label="여성"/>
<form:radiobutton path="gender" value="etc" label="기타"/>
</p>
<p>취미 :
<form:checkboxes items="${user.hobbyMap}" path="hobbyArray"/>
</p>
<p>
<form:checkbox path="hobby" value="kmj" label="Music"/>
<form:checkbox path="hobby" value="ksy" label="FootBall"/>
<form:checkbox path="hobby" value="esb" label="showping"/>
</p>
<p>
<form:checkbox path="hobbyArray" value="kmj" label="Music"/>
<form:checkbox path="hobbyArray" value="ksy" label="FootBall"/>
<form:checkbox path="hobbyArray" value="esb" label="showping"/>
</p>
<p>보유자동차 :
<form:checkboxes items="${user.carMap}" path="carArray"/>
</p>
<p>국적 : (String nationality)
<form:select path="nationality" items="${user.notionalityCodeMap}"></form:select>
</p>
<!-- 국적을 공통 코드로부터 가져와보자 -->
<select id="nationality" name="nationality">
<c:forEach var="comCodeDetailVO" items="${comCodeVO.comCodeDetailVOList}" varStatus="stat">
<option value="${comCodeDetailVO.comCodeDetail}">${comCodeDetailVO.comCodeDetailNm}</option>
</c:forEach>
</select>
<p>
<form:button name="register">등록</form:button>
</p>
</form:form>
- 검증오류 발생 시 확인 (null값일때, 이메일, 날짜)
MemberController.java
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import kr.or.ddit.mapper.ComCodeMapper;
import kr.or.ddit.service.MemberService;
import kr.or.ddit.vo.Address;
import kr.or.ddit.vo.Card;
import kr.or.ddit.vo.ComCodeVO;
import kr.or.ddit.vo.Member;
import lombok.extern.slf4j.Slf4j;
import oracle.jdbc.proxy.annotation.Post;
@Slf4j
@Controller
public class MemberController {
// DI(의존성 주입) / IoC(제어의 역전)
@Autowired
MemberService memberService;
@Autowired
ComCodeMapper comCodeMapper;
//1) 컨트롤러 메서드의 매개변수로 자바빈즈 객체가 전달이 되면 기본적으로 다시 화면으로 전달
//컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는 다시 화면으로 폼 객체를 전달함
//2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
//3) ModelAttribute 애너테이션으로 폼 객체의 속성명을 직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member,
Model model) {
// model.addAttribute("member", new Member());
//폼 객체의 프로퍼티에 값을 지정
// member.setUserId("gaeddongi");
// member.setUserName("개똥이");
member.setCoin(1000);
member.setPassword("java");
member.setIntroduction("저는 개똥이 입니다.\n왜일까요?");
//모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면에 전달
Map<String, String> hobbyMap = new HashMap<String, String>();
hobbyMap.put("kmj", "Music");
hobbyMap.put("ksy", "FootBall");
hobbyMap.put("esb", "showping");
member.setHobbyMap(hobbyMap);
//Music을 미리 체크 처리
String[] hobbyArray = {"ksy"};
member.setHobbyArray(hobbyArray);
member.setHobby("ksy");
//보유자동차
Map<String,String> carMap = new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
//volvo을 미리 체크 처리
String[] carArray = {"volvo"};
member.setCarArray(carArray);
//성별
Map<String,String> genderCodeMap = new HashMap<String, String>();
genderCodeMap.put("Male", "남성");
genderCodeMap.put("Female", "여성");
genderCodeMap.put("etc", "기타");
member.setGenderCodeMap(genderCodeMap);
//미리 선택 처리
member.setGender("Female");
//11. 셀렉트 박스 요소
//국적 선택
Map<String, String> notionalityCodeMap =
new HashMap<String, String>();
notionalityCodeMap.put("Korea", "대한민국");
notionalityCodeMap.put("Germany", "독일");
notionalityCodeMap.put("Australia", "오스트레일리아");
member.setNotionalityCodeMap(notionalityCodeMap);
//국적 미리 선택
member.setNationality("Korea");
//국적을 공통코드로부터 가져와보자
ComCodeVO comCodeVO = this.comCodeMapper.getComCode("natn");
log.info("registerFOrm05->comCodeVO : " + comCodeVO);
model.addAttribute("comCodeVO", comCodeVO);
//forwarding
return "registerForm05";
}
/*
입력값 검증을 하기 위해서는 메서드 매개변수에 도메인 클래스를 정의하고 골뱅이Validated를 지정
입력값 검증 대상의 도메인 클래스 직후에 BindingResult를 정의함
BindingResult에는 요청 데이터의 바인딩 오류와 입력값 검증 오류 정보가 저장됨
*/
@PostMapping("/registerForm01Post")
public String registerForm01Post(
@Validated @ModelAttribute("user") Member member,
BindingResult brResult,
Model model
) {
log.info("registerForm01Post->member : " + member);
//1)true : 문제 있음 / 2)false : 문제 없음
log.info("brResult.hasErrors() : " + brResult.hasErrors());
/* 2. 입력값 검증 결과
입력값 검증 대상의 도메인 클래스 직후에 BindingResult를 정의함
BindingResult에는 요청 데이터의 바인딩 에러와 입력값 검증 에러 정보가 저장됨
*/
//검증 오류 발생 시 => insert 안함
if(brResult.hasErrors()) {//true : 오류 발생
//검사 결과 오류 확인
List<ObjectError> allErrors = brResult.getAllErrors();
//객체와 관련된 오류
List<ObjectError> globalErrors
= brResult.getGlobalErrors();
//멤버변수와 관련된 오류
List<FieldError> fieldErrors
= brResult.getFieldErrors();
for (ObjectError objectError : allErrors) {
log.info("allError : " + objectError);
}
for (ObjectError objectError : globalErrors) {
log.info("globalErrors : " + objectError);
}
for (FieldError fieldError : fieldErrors) {
log.info("fieldErrors : " + fieldError);
}
/*----*/
//폼 객체의 프로퍼티에 값을 지정
member.setCoin(1000);
member.setPassword("java");
member.setIntroduction("저는 개똥이 입니다.\n왜일까요?");
//모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면(jsp)에 전달
Map<String,String> hobbyMap =
new HashMap<String, String>();
hobbyMap.put("Music", "음악");
hobbyMap.put("FootBall", "축구");
hobbyMap.put("Book", "도서");
hobbyMap.put("Travel", "여행");
member.setHobbyMap(hobbyMap);
//Music을 미리 체크 처리
String[] hobbyArray = {"kmj"};
member.setHobbyArray(hobbyArray);
member.setHobby("kmj");
//보유자동차
Map<String,String> carMap =
new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
//volvo를 미리 체크 처리
String[] carArray = {"volvo"};
member.setCarArray(carArray);
//성별
Map<String,String> genderCodeMap =
new HashMap<String, String>();
genderCodeMap.put("Male", "남성");
genderCodeMap.put("Female", "여성");
genderCodeMap.put("etc", "기타");
member.setGenderCodeMap(genderCodeMap);
//미리 선택 처리
member.setGender("Female");
//11. 셀렉트 박스 요소
//국적 선택
Map<String,String> notionalityCodeMap =
new HashMap<String, String>();
notionalityCodeMap.put("Korea", "대한민국");
notionalityCodeMap.put("Germany", "독일");
notionalityCodeMap.put("Australia", "오스트레일리아");
notionalityCodeMap.put("Canada", "캐나다");
member.setNotionalityCodeMap(notionalityCodeMap);
//국적 미리 선택
member.setNationality("Korea");
//국적을 공통코드로부터 가져와보자
ComCodeVO comCodeVO = this.comCodeMapper.getComCode("natn");
log.info("registerForm05->comCodeVO : " + comCodeVO);
model.addAttribute("comCodeVO", comCodeVO);
/*----*/
//forwarding
return "registerForm05";
}
/*----*/
//폼 객체의 프로퍼티에 값을 지정
member.setCoin(1000);
member.setPassword("java");
member.setIntroduction("저는 개똥이 입니다.\n왜일까요?");
//모델에 Map 타입의 데이터를 생성하여 추가한 후에 화면(jsp)에 전달
Map<String,String> hobbyMap =
new HashMap<String, String>();
hobbyMap.put("Music", "음악");
hobbyMap.put("FootBall", "축구");
hobbyMap.put("Book", "도서");
hobbyMap.put("Travel", "여행");
member.setHobbyMap(hobbyMap);
//Music을 미리 체크 처리
String[] hobbyArray = {"kmj"};
member.setHobbyArray(hobbyArray);
member.setHobby("kmj");
//보유자동차
Map<String,String> carMap =
new HashMap<String, String>();
carMap.put("qm5", "qm5");
carMap.put("sm6", "sm6");
carMap.put("volvo", "volvo");
member.setCarMap(carMap);
//volvo를 미리 체크 처리
String[] carArray = {"volvo"};
member.setCarArray(carArray);
//성별
Map<String,String> genderCodeMap =
new HashMap<String, String>();
genderCodeMap.put("Male", "남성");
genderCodeMap.put("Female", "여성");
genderCodeMap.put("etc", "기타");
member.setGenderCodeMap(genderCodeMap);
//미리 선택 처리
member.setGender("Female");
//11. 셀렉트 박스 요소
//국적 선택
Map<String,String> notionalityCodeMap =
new HashMap<String, String>();
notionalityCodeMap.put("Korea", "대한민국");
notionalityCodeMap.put("Germany", "독일");
notionalityCodeMap.put("Australia", "오스트레일리아");
notionalityCodeMap.put("Canada", "캐나다");
member.setNotionalityCodeMap(notionalityCodeMap);
//국적 미리 선택
member.setNationality("Korea");
//국적을 공통코드로부터 가져와보자
ComCodeVO comCodeVO = this.comCodeMapper.getComCode("natn");
log.info("registerForm05->comCodeVO : " + comCodeVO);
model.addAttribute("comCodeVO", comCodeVO);
/*----*/
return "registerForm05";
}
}
registerForm05.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<h2>Spring Form</h2>
<!-- modelAttribute 속성에 폼 객체의 속성명을 지정함 -->
<form:form modelAttribute="user" method="post" action="/registerForm01Post">
<hr/>
<p><form:label path="userId">ID : </form:label>
<form:input path="userId" placeholder="아이디" />
<font color="red"><form:errors path="userId" /></font>
</p>
<p><form:label path="userName">이름 : </form:label>
<form:input path="userName" placeholder="이름" />
<font color="red"><form:errors path="userName" /></font>
</p>
<p><form:label path="password">패스워드 : </form:label>
<form:input path="password" placeholder="패스워드" />
</p>
<p>
<form:hidden path="coin"/>
</p>
<p>패스워드 :
<input type="password" name="password" id="password"/>
<form:input path="password" /> <!-- 바인딩되어서 값이 들어옴 -->
</p>
<p>소개글 :
<form:textarea path="introduction" rows="6" cols="30"/>
</p>
<p>개발자여부 : (String)
<form:checkbox path="developer" value="Y" label="Y/없음"/>
</p>
<p>외국인여부 : (boolean)
<form:checkbox path="foreigner" value="false" label="trud/false"/>
</p>
<p><!-- <label for="email" -->
<form:label path="email">이메일 주소 :</form:label>
<!-- <input type="text" id="email" name="email" -->
<form:input path="email" />
<font color="red"><form:errors path="email" /></font>
</p>
<p><form:label path="dateOfBirth">생일 :</form:label>
<form:input path="dateOfBirth" placeholder="ex)2020-03-01" />
<font color="red"><form:errors path="dateOfBirth" /></font>
</p>
<p>성별 : (String gender)
<form:radiobuttons items="${user.genderCodeMap}" path="gender"/>
</p>
<p>
<form:radiobutton path="gender" value="Male" label="남성"/>
<form:radiobutton path="gender" value="Female" label="여성"/>
<form:radiobutton path="gender" value="etc" label="기타"/>
</p>
<p>취미 :
<form:checkboxes items="${user.hobbyMap}" path="hobbyArray"/>
</p>
<p>
<form:checkbox path="hobby" value="kmj" label="Music"/>
<form:checkbox path="hobby" value="ksy" label="FootBall"/>
<form:checkbox path="hobby" value="esb" label="showping"/>
</p>
<p>
<form:checkbox path="hobbyArray" value="kmj" label="Music"/>
<form:checkbox path="hobbyArray" value="ksy" label="FootBall"/>
<form:checkbox path="hobbyArray" value="esb" label="showping"/>
</p>
<p>보유자동차 :
<form:checkboxes items="${user.carMap}" path="carArray"/>
</p>
<p>국적 : (String nationality)
<form:select path="nationality" items="${user.notionalityCodeMap}"></form:select>
</p>
<select id="nationality" name="nationality">
<c:forEach var="comCodeDetailVO" items="${comCodeVO.comCodeDetailVOList}" varStatus="stat">
<option value="${comCodeDetailVO.comCodeDetail}">${comCodeDetailVO.comCodeDetailNm}</option>
</c:forEach>
</select>
<p>
<form:button name="register">등록</form:button>
</p>
</form:form>
Member.java
package kr.or.ddit.vo;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class Member {
//입력값 검증 규칙을 지정함
@NotBlank
private String userId;
// 여러 개의 입력값 검증 규칙을 지정할 수 있음
@NotBlank(message="사용자명을 입력해주세요.")
@Size(max=3, message="최대 3글자까지만 작성해주세요.")
private String userName;
private String password = "1234";
@DateTimeFormat(pattern="yyyy-MM-dd") private Date regDate;
private int coin;
//스프링폼에서 radiobuttons의 path로 사용
private String gender;
//스프링폼에서 radiobuttons의 item로 사용
private Map<String,String> genderCodeMap;
//이메일 주소 형식인지 검사
@NotBlank(message="이메일주소를 입력해주세요.")
@Email(message="올바른 이메일주소를 입력해주세요.")
private String email;
private String birthDay;
//스프링폼에서 select의 path로 사용
private String nationality;
//스프링폼에서 select의 item로 사용
private Map<String,String> notionalityCodeMap;
private String cars;
private String[] carArray;
//스프링폼에서 checkboxes의 path로 사용
private ArrayList<String> carList;
//스프링폼에서 checkboxes의 item으로 사용
private Map<String, String> carMap;
private String hobby;
private String[] hobbyArray;
//스프링폼에서 checkboxes의 path으로 사용
private ArrayList<String> hobbyList;
//스프링폼에서 checkboxes의 item으로 사용
private Map<String,String> hobbyMap;
private String developer;
private boolean foreigner;
private String introduction;
@DateTimeFormat(pattern="yyyy-MM-dd")
@Past(message="과거 날짜를 입력해주세요")
private Date dateOfBirth;
// 중첩된(nested) 자바빈즈
// MEMBER : ADDRESS = 1 : 1
private Address address;
// MEMBER : CARD = 1 : N
private List<Card> cardList;
//스프링프레임워크에서 제공하는 MultipartFile 파일객체타입
private MultipartFile picture;
//<input type="file" name="pictures" multiple>
// 위의 input과 name 값이 변수명과 같아야 함
private MultipartFile[] pictures;
}
- 검증오류 발생 시 확인 (중첩된 자바빈즈)
Member.java
추가 또는 변경함
// 중첩된(nested) 자바빈즈
// MEMBER : ADDRESS = 1 : 1
/*
4. 중첩된 자바빈즈 입력값 검증
중첩된 자바빈즈와 자바빈즈의 컬렉션에서 정의한 프로퍼티에 대해
입력값 검증을 할 때는 골뱅이Valid를 지정함
*/
//중첩된 자바빈즈의 입력값 검증을 지정함
@Valid
private Address address;
// MEMBER : CARD = 1 : N
//자바빈즈 컬렉션의 입력값 검증을 지정함
@Valid
private List<Card> cardList;
Address.java
package kr.or.ddit.vo;
import org.hibernate.validator.constraints.NotBlank;
import lombok.Data;
@Data
public class Address {
//Member 자바빈 클래스의 프로퍼티
private String userId;
@NotBlank(message="우편번호를 입력해주세요")
private String postCode; //우편번호
@NotBlank(message="주소를 입력해주세요")
private String location; //주소
}
Card.java
package kr.or.ddit.vo;
import java.util.Date;
import javax.validation.constraints.Future;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;
@Data
public class Card {
//Member 자바빈 클래스의 프로퍼티
private String userId;
@NotBlank(message = "카드 번호를 입력해주세요")
private String no;
@Future(message = "미래의 날짜를 입력해주세요")
@DateTimeFormat(pattern = "yyyyMMdd")
private Date validMonth;
}
registerForm05.jsp
</form:form> 안에 추가
<p>주소 :
1) 우편번호 : <form:input path="address.postCode" />
<font color="red"><form:errors path="address.postCode" /></font>
<br />
2) 주소 : <form:input path="address.location" />
<font color="red"><form:errors path="address.location" /></font>
</p>
<p>카드 :
<form:input path="cardList[0].no" placeholder="카드번호"/>
<font color="red"><form:errors path="cardList[0].no" /></font>
<form:input path="cardList[0].validMonth" placeholder="ex)20240517"/>
<font color="red"><form:errors path="cardList[0].validMonth" /></font>
<br />
<form:input path="cardList[1].no" placeholder="카드번호"/>
<font color="red"><form:errors path="cardList[1].no" /></font>
<form:input path="cardList[1].validMonth" placeholder="ex)20240517"/>
<font color="red"><form:errors path="cardList[1].validMonth" /></font>
</p>
@Range : int 할 때 씀
골뱅이Range(min = 1, message="정렬순서를 입력해주세요")
: private int orderNo;
- 문자열을 다룰 때 사용
Size(min=?, max=?) // 최소 길이, 최대 길이 제한
- 숫자를 다룰 때 사용
Positive // 양수만 허용
PositiveOrZero // 양수와 0만 허용
Negative // 음수만 허용
NegativeOrZero // 음수와 0만 허용
- 순서
1. 스프링폼으로
- form:form
- form:input
- form:errors path.. 처리
2. 컨트롤러
- @Validated
- BindingResult -> brResult.hasErrors()
3. 자바빈 클래스(VO)
- String 타입 : NotBlank
- int 타입 : Range(min=1,message..)
- 실습
LprodController.java
package kr.or.ddit.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import kr.or.ddit.service.LprodService;
import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/lprod")
@Controller
public class LprodController {
//DI(의존성 주입) / IoC(제어의 역전)
@Autowired
LprodService lprodService;
/*
요청URI : /lprod/create
요청파라미터 :
요청방식 : get
*/
@RequestMapping(value="/create",method=RequestMethod.GET)
public ModelAndView create(LprodVO lprodVO) {
ModelAndView mav = new ModelAndView();
// 데이터
mav.addObject("title", "상품분류 등록");
// jsp
// /WEB-INF/views/ + lprod/create + .jsp
mav.setViewName("lprod/create");
return mav;
}
/*
요청URI : /lprod/create
요청파라미터 : {lprodId=14, lprodGu=P501, lprodNm=분식류}
요청방식 : post
*/
@RequestMapping(value="/create", method=RequestMethod.POST)
public String createPost(
@Validated @ModelAttribute("lprod") LprodVO lprodVO,
BindingResult brResult) {
log.info("createPost->lprodVO : " + lprodVO);
log.info("createPost -> brResult.hasErrors() : " + brResult.hasErrors());
if(brResult.hasErrors()) {//true(오류 발생)
//검사 결과 오류 확인
List<ObjectError> allErrors = brResult.getAllErrors();
//객체와 관련된 오류
List<ObjectError> globalErrors
= brResult.getGlobalErrors();
//멤버변수와 관련된 오류
List<FieldError> fieldErrors
= brResult.getFieldErrors();
for(ObjectError objectError : allErrors) {
log.info("allError : " + objectError);
}
for(ObjectError objectError : globalErrors) {
log.info("globalError : " + objectError);
}
for(FieldError fieldError : fieldErrors) {
log.info("fieldError : " + fieldError);
}
//forwording
return "lprod/create";
}
int result = this.lprodService.createPost(lprodVO);
log.info("createPost->result : " + result);
// ModelAndView mav = new ModelAndView();
// // redirect : 새로운 URI 요청
// mav.setViewName("redirect:/lprod/create");
return "redirect:/lprod/create";
}
}
LprodVO.java
package kr.or.ddit.vo;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class LprodVO {
@Range(min=1, message = "상품분류 아이디를 입력해주세요")
private int lprodId;
@NotBlank(message = "상품분류 코드를 입력해주세요")
private String lprodGu;
@NotBlank(message = "상품분류 명을 입력해주세요")
private String lprodNm;
//상품분류코드 : 상품 = 1 : N
//LPROD :PRODUCT = 1 : N
@Valid
private List<ProductVO> productVOList;
private MultipartFile[] pictures;
}
ProductVO.java
package kr.or.ddit.vo;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;
import lombok.Data;
@Data
public class ProductVO {
@NotBlank(message="상품아이디를 입력해주세요")
private String productId;
@NotBlank(message="상품명을 입력해주세요")
private String pname;
@Range(min=1,message="상품 가격을 입력해주세요")
private int unitPrice;
private String description;
private String manufacturer;
private String category;
private int unitsInStock;
private String condition;
private String filename;
private int quantity;
}
create.jsp
form: 태그로 변경 후 function fn_check() 추가, 등록 버튼 변경
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery.min.js"></script>
<title>Insert title here</title>
<script type="text/javascript">
$(function(){
// 상품분류 아이디 자동생성
getLprodId();
$("#btnAuto").on("click",function(){
let lprodGu = $("input[name='lprodGu']").val();
if($.trim(lprodGu)==""){
alert("상품분류코드를 작성해주세요");
$("input[name='lprodGu']").focus();
return;
}
$(".pArea").each(function(idx,row){
$(this).children().first().val(lprodGu+(idx+1));
});
});
// 상품 추가
/*
before : 선택한 요소의 앞에 내용 삽입
after : 선택한 요소의 뒤에 내용 삽입
prepend : 선택한요소의 자식요소 앞에 내용삽입
append : 선택한요소의 자식요소 뒤에 내용삽입
*/
$("#btnPlus").on("click",function(){
let pAreaLen = $(".pArea").length;
let str = "";
str += "<p class='pArea'>";
str += "<input type='text' name='productVOList["+pAreaLen+"].productId' id='productId"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='상품아이디'>";
str += "<input type='text' name='productVOList["+pAreaLen+"].pname' id='pname"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='상품명'>";
str += "<input type='text' name='productVOList["+pAreaLen+"].unitPrice' id='unitPrice"+pAreaLen+"' class='form-control col-sm-3 float-left clsUnitPrice' placeholder='가격'>";
str += "</p>";
str += "<p style='clear:both;'></p>";
$("#btnSubmit").parent().before(str);
});
$("#btnMinus").on("click",function(){
let pAreaLen = $(".pArea").length;
console.log("pAreaLen : ", pAreaLen);
if(pAreaLen < 2) {
alert("최소 하나는 존재해야 합니다.");
return;
}
// next는 선택한 요소의 다음 형제 요소를 반환
$(".pArea").last().next().remove();
$(".pArea").last().remove();
});
$("#btnSubmit").on("click",function(){
let lprodId = $("input[name='lprodId']").val();
let lprodGu = $("input[name='lprodGu']").val();
let lprodNm = $("input[name='lprodNm']").val();
//<form></form>
let formData = new FormData();
formData.append("lprodId",lprodId);
formData.append("lprodGu",lprodGu);
formData.append("lprodNm",lprodNm);
//<input type="file"...
let fileObj = $("#pictures");
//선택된 파일객체들
let files = fileObj[0].files;
console.log("files.length : ", files.length); //3
for(let i=0;i<files.length; i++){ //0,1,2
formData.append("pictures",files[i]); // 파일이 n개일때 하나씩 append 함
}
//<p class="pArea".. 반복됨
$(".pArea").each(function(idx,data){
let productId = $(this).children().eq(0).val();
let pname = $(this).children().eq(1).val();
let unitPrice = $(this).children().eq(2).val();
formData.append("productVOList["+idx+"].productId", productId);
formData.append("productVOList["+idx+"].pname", pname);
formData.append("productVOList["+idx+"].unitPrice", unitPrice);
});
//JSON오브젝트
let data = {
"lprodId":lprodId,
"lprodGu":lprodGu,
"lprodNm":lprodNm
};
console.log("data : ", data);
});
});// end 달러(function)
function getLprodId(){
//아작나서유..(피)씨다타써
$.ajax({
url:"/lprod/getLprodId",
type:"post",
dataType:"text",
success:function(result){
console.log("result : ", result);
$("input[name='lprodId']").val(result);
}
});
}
function fn_check(){
console.log("왔다");
let flag = 1;
/* 동적으로 생성된 것들이 있기에 */
$(".clsUnitPrice").each(function(){
let str = $(this).val();
console.log("str : " + str);
if(str=="") {
alert("상품 가격을 입력해주세요");
flag = 0;
}
});
if(flag==1) {
return true;
} else {
return false;
}
}
</script>
</head>
<body>
<h1>상품분류 등록</h1>
<!-- onsubmit : submit 버튼 클릭 시 핸들러함수로부터 true를 리턴 받아야 submit이 완료됨 -->
<form:form modelAttribute="lprodVO" action="/lprod/create" method="post" enctype="multipart/form-data"
onsubmit="return fn_check()">
<p>
<form:input path="lprodId" placeholder="상품분류 아이디(ex. 14)" readonly="true"/>
<code><form:errors path="lprodId" /></code>
</p>
<p>
<form:input path="lprodGu" placeholder="상품분류 코드(ex. P501)" required="true" />
<code><form:errors path="lprodGu" /></code>
<button type="button" name="btnAuto" id="btnAuto" class="btn btn-primary btn-xs col-sm-2">상품코드 자동생성</button>
</p>
<p>
<form:input path="lprodNm" placeholder="상품분류 명(ex. 분식류)" />
<code><form:errors path="lprodNm" /></code>
</p>
<hr />
<p><input type="file" name="pictures" id="pictures" multiple /></p>
<hr />
<p id="pFunc">
<button type="button" class="btn btn-info btn-sm col-sm-1" id="btnPlus" >+</button>
<button type="button" class="btn btn-danger btn-sm col-sm-1" id="btnMinus" >-</button>
</p>
<!--
1) + : 영역이 하나 생김
2) - : 마지막 영역 삭제
-->
<p class="pArea">
<form:input path="productVOList[0].productId" id="productId0" class="form-control col-sm-3 float-left" placeholder="상품아이디" />
<code><form:errors path="productVOList[0].productId" /></code>
<form:input path="productVOList[0].pname" id="pname0" class="form-control col-sm-3 float-left" placeholder="상품명" />
<code><form:errors path="productVOList[0].pname" /></code>
<input type="number" name="productVOList[0].unitPrice" id="unitPrice0" class="form-control col-sm-3 float-left clsUnitPrice" placeholder="가격" />
<code><form:errors path="productVOList[0].unitPrice" /></code>
</p>
<p style="clear:both;"></p>
<p>
<form:button name="btnSubmit">등록</form:button>
</p>
</form:form>
</body>
</html>
'스프링 프레임워크' 카테고리의 다른 글
[스프링 프레임워크] 25장 AOP (0) | 2024.05.16 |
---|---|
[스프링 프레임워크] 24장 페이징 처리 (0) | 2024.05.14 |
[스프링 프레임워크] 22장 공통 코드로 관리 (0) | 2024.05.10 |
[스프링 프레임워크] 21장 스프링 폼 태그 라이브러리 (0) | 2024.05.10 |
[스프링 프레임워크] 20장 모델을 통한 데이터 전달 (0) | 2024.05.09 |