-
0918. Spring Form Tag 라이브러리Spring 2023. 9. 20. 09:04
Spring Framework JSP Form Tag Library
○ 모델 객체를 Form에 자동으로 바인딩해줌
○ taglib 디렉티브
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
select, checkbox, radio button 등을 사용할 때 편하다.
form 태그 라이브러리를 사용하면 유효성 검사로 에러메시지 출력을 쉽게 할 수 있다.
v Spring Framework JSP Form Tag Library
새 패키지 ex03
starter에서 복사 후 pom.xml에서 ex03으로 수정
views에 layout폴더 복사(헤더 등 jsp)
domain 폴더 생성 후 MemberVO (난 @Data도 추가)
Contoller 폴더에 TestController 생성
- join 사용
- get처리에도 모델객체 제시
- post에 errors 라는 매개변수를 추가하는데 error를 처리하는 객체로 나중에 유효성 검사할 때 쓸 예정
- return을 리다이렉트 하지 않고 view의 이름을 제시(get 요청의 view이름 : test/join)
void이므로 리턴타입이 get요청과 동일한 url의 이름으로 줌.
*form의 유효성을 확인하는 거니까
form에러가 있다면 다시 form으로 돌아가서 입력을 받으려고 form의 view를 리턴. (리다이렉트 쓰지 않는 이유)
복원을 어떻게 할지 보는 것
package org.galapagos.controller; import org.galapagos.domain.MemberVO; import org.springframework.stereotype.Controller; 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.AllArgsConstructor; import lombok.extern.log4j.Log4j; @Controller @RequestMapping("/test") @Log4j @AllArgsConstructor public class TestController{ @GetMapping("/join") public void join(@ModelAttribute("member") MemberVO member) { } @PostMapping("/join") public String join(@ModelAttribute("member") MemberVO member, Errors errors) { return "test/join"; // return "redirect:/"; } }
views에 test폴더 생성 후 join.jsp 생성
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <form:form modelAttribute="member" cssClass="form"> <input type="submit"/> </form:form>
HomeController 수정 redirect
package org.galapagos.controller; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @RequestMapping(value = "/", method = RequestMethod.GET) public String home(Locale locale, Model model) { return "redirect:/test/join"; //test join으로 가도록 리다이렉트 후 재기동 } }
모델객체(MemberVO)의 프로퍼티명 : patn="no" => 접근 : getNo()
type은 hidden
프로퍼티명이 id랑 name의 이름으로 들어감
getNo한 값이 value의 속성값 부분에 디폴트로 들어감
배열이거나 리스트이면 타이틀과 value가 같은 값인데
타이틀과 value를 다르게 해 주려면 Map을 사용하면 된다.
=> Controller 적용
TestController 전체 코드
package org.galapagos.controller; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.galapagos.domain.MemberVO; 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.log4j.Log4j; @Controller @RequestMapping("/test") @Log4j public class TestController { private int[] years = {2000, 2001, 2002, 2003}; private String[] hobbies = { "야구", "축구", "배구" }; private String[] genders = { "남자", "여자" }; @GetMapping("/join") //여기 속성 안 넣으면 500 에러 public void join( @ModelAttribute("member") MemberVO member, Model model ) { //model.addAttribute("years", years); //model.addAttribute("roles", getRoles()); //model.addAttribute("hobbies", hobbies); //model.addAttribute("genders", genders); } @PostMapping("/join") public String join( @ModelAttribute("member") MemberVO member, Errors errors, Model model) { //model.addAttribute("years", years); //model.addAttribute("roles", getRoles()); //model.addAttribute("hobbies", hobbies); return "test/join"; // forwarding --> form을 다시 입력하도록 함(이전 값으로 복사) // return "redirect:/"; } @ModelAttribute("years") public List<Integer> years() { // 자동으로 메서드 호출됨, 리턴값이 모델에 추가됨 List<Integer> years = new ArrayList<>(); for (int i = 1950; i <= 2023; i++) { years.add(i); } return years; } @ModelAttribute("roles") public Map<String, String> getRoles() { Map<String, String> map = new LinkedHashMap<>(); map.put("ROLE_ADMIN", "Admin"); map.put("ROLE_MANAGER", "관리자"); map.put("ROLE_MEMBER", "일반회원"); return map; } @ModelAttribute("hobbies") private String[] hobbies() { String[] hobbies = {"야구", "축구", "배구"}; return hobbies; } @ModelAttribute("genders") private String[] genders() { String[] genders = { "남자", "여자" }; return genders; } /* * @ModelAttribute("hobbies") public Map<String, String> getHobbies() { * Map<String, String> hobbies = new LinkedHashMap<>(); hobbies.put("BASEBALL", * "야구"); hobbies.put("FOOTBALL", "축구"); hobbies.put("VOLLYBALL", "배구"); return * hobbies; } */ }
join.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <%@ include file="../layouts/header.jsp"%> <%-- 개별 페이지 --%> <h1>페이지 타이틀</h1> <form:form modelAttribute="member" cssClass="form"> <form:hidden path="no" /> <div class="form-group"> <form:label path="username"> 사용자 ID: </form:label> <form:input path="username" cssClass="form-control" /> </div> <div class="form=group"> <form:label path="password"> 비밀번호: </form:label> <form:password path="password" cssClass="form-control" /> </div> <div class="form-group"> <form:label path="password2"> 비밀번호 확인: </form:label> <form:password path="password2" cssClass="form-control" /> </div> <div class="form-group"> <form:label path="email"> 이메일: </form:label> <form:input path="email" cssClass="form-control" /> </div> <div class="from-group"> <form:label path="birthYear">출생년도:</form:label> <form:select path="birthYear" cssClass="form-control" style="width=300px"> <form:option value="">---</form:option> <form:options items="${years }" /> </form:select> </div> <div class="form-group" style="width: 120px"> <form:label path="">역할</form:label> <form:select path="role" cssClass="form-control"> <option value="">역할선택</option> <form:options items="${roles }" /> </form:select> </div> <div class="form-group"> <label>취미: </label> <form:checkboxes path="hobbies" items="${hobbies }" cssClass="ml-4 mr-2" /> </div> <div class="form-group"> <label>성별:</label> <form:radiobuttons path="gender" items="${genders}" cssClass="ml-4 mr-2" /> </div> <div class="form-check"> <form:label path="agreement" cssClass="form-check-label"> <form:checkbox path="agreement" cssClass="form-check-input" /> 동의합니다. </form:label> </div> <div class="form-group"> <form:label path="intro">인사말:</form:label> <form:textarea path="intro" cssClass="form-control" rows="5"/> </div> <input type="submit" /> </form:form> <%@ include file="../layouts/footer.jsp"%>
유효성 검사
form으로 입력받고 submit 한 이후 controller에서 제출 폼 기반으로 VO객체가 만들어진다.
이 때 VO 객체가 올바른 값(valid)을 다 가지고 있는지 체크.
만약 올바르지 않다면(invalid) 수정을 요구 : 처리 후 redirect
이럴 때 스프링에서 제공하는 기능
의존성 주입으로 사용
Validator를 base pom.xml에 추가하자
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.7.Final</version> </dependency>
VO 객체에 유효성 검사를 위한 어노테이션을 사용가능하다.
VO객체의 멤버변수에 붙이는 어노테이션들이다.
⭐필수항목 처리할 때 자주 쓰는 게 NotBlank
⭐비밀번호 최소 자리수, 사용자 ID 최대 자리수 : Size(min=,max=)
public class MemberVO { private Long no; @NotBlank(message="사용자 ID는 필수 항목입니다.") @Size(min=5, message="사용자 ID는 5자 이상이어야 합니다.") private String username; @NotBlank(message="비밀번호는 필수 항목입니다.") private String password; @NotBlank(message="비밀번호 확인은 필수 항목입니다.") private String password2; @NotBlank(message="email은 필수 항목입니다") private String email;
Errors 인터페이스
@Valid 뒤로 Errors 안 붙이면 안 된다. (다른 곳에 있으면 오동작한다.)
@PostMapping("/join") public String join( @Valid @ModelAttribute("member") MemberVO member, Errors errors, Model model) { log.info("POST: " + member); if(errors.hasErrors()) { return "test/join"; // forwarding --> form을 다시 입력하도록 함(이전 값으로 복사) } // DB 처리 작업 return "redirect:/"; }
에러인지 확인도 해야 하므로 이 코드를 삽입
<form:errors path="username" cssClass="error"/>
join.jsp 상단에 빨간 글씨로 나오게 css style 적용
<%@ include file="../layouts/header.jsp"%> <style> .error { color: red; font-size: 0.9rem; } </style> <%-- 개별 페이지 --%> <h1>페이지 타이틀</h1>
비밀번호 일치 여부 에러메시지 출력 코드
@PostMapping("/join") public String join( @Valid @ModelAttribute("member") MemberVO member, Errors errors, Model model) { log.info("POST: " + member); if(!member.getPassword().equals(member.getPassword2())) { errors.rejectValue("password2", "비밀번호확인 에러", "비밀번호 확인이 일치하지 않습니다."); } if(errors.hasErrors()) { return "test/join"; // forwarding --> form을 다시 입력하도록 함(이전 값으로 복사) } // DB 처리 작업 return "redirect:/"; }
이메일 양식 맞추기
VO 클래스에 NotBlank 밑에 넣기
@Email(message="email 양식에 맞지 않습니다.")
전역 에러 추가하기
errors.reject("로그인 실패", "사용자 ID 또는 비밀번호가 일치하지 않습니다.");
각 필드(폼)의 유효성 검사는 통과했으나 특정 항목에 종속된 에러가 아닌 경우
=> 로그인 시 사용자 id나 비밀번호가 틀린 경우
"DB의 내용과 다를 때"
=> form 바깥에 출력시킴
--
ex02에 적용시키기
1. 검색할 때 썼던 폼을 스프링 폼 태그로 적용하기
list.jsp에 태그 라이브러리 입력
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form> => <form:form>로 여는, 닫는 태그 둘 다 수정
modelAttribute "cri"로 추가
form:hidden 추가
pageNum
amount 추가하고 밑에 input hidden 지우기
페이지 소스 보기 누르면 이렇게 보인다.
TravelController
searchTypes 에서 map으로 가져오면 list.jsp의 form 태그를 줄일 수 있다.
@ModelAttribute("searchTypes") public Map<String, String> searchTypes(){ Map<String, String> map = new LinkedHashMap<String, String>(); map.put("", "--검색대상선택--"); map.put("R", "권역"); map.put("T", "제목"); map.put("D", "내용"); map.put("TD", "제목+내용"); map.put("TR", "권역+제목"); map.put("TRD", "권역+제목+내용"); return map; }
div 태그 분리해서 search_bar.jsp 로 옮기기
(search.js 스크립트랑 같이)
jsp
list(board도 같이), register, search_bar,
BoardController, TravelController
main.css (error 들어갔는지)
TravelVO 에러 처리
'Spring' 카테고리의 다른 글
0926. 답글 처리(서버 + 클라이언트) (0) 2023.09.27 0925. 댓글 화면 출력(댓글 작성, 수정-확인 및 취소, 삭제) (0) 2023.09.25 0922. 댓글처리 Ajax, REST API (0) 2023.09.22 0915. 여행 페이지를 만들자 (2) 2023.09.15