ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 

    form 태그 종류

     


    새 패키지 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>

    에러 css

    비밀번호 일치 여부 에러메시지 출력 코드

    	@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 에러 처리

     

     

Designed by Tistory.