Spring 실습

Spring 실습 14일차(모델을 통한 데이터 전달)

choco2706 2024. 5. 10. 18:37

어제 작성했던 cardList와 address를 출력

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- <p>${member}</p> --%>
<!--  --MEMBER : ADDRESS = 1 : 1 (association)
   -- member.address.userId(자바빈객체.중첩된 자바빈객체.프로퍼티)
   address = Address(userId = null , posetCode = 080908 
         , location = seoul),
      -->
<!--  model.addAttribute("member", --> 
<p>member.address.postCode
    : ${member.address.postCode}
</p>
<p>member.address.location
    : ${member.address.location}
</p>
<h4>member.cardList</h4>
<c:forEach var="card" items="${member.cardList}" varStatus="stat">
    <p>card${stat.count}.no : ${card.no}</p>
    <p>card${stat.count}.validMonth : ${card.validMonth}</p>
</c:forEach>

실행 결과

 

c:forEach를 통해 cardList에 담긴 객체들을 card라는 이름으로 하나하나 꺼내온다.

stat.count는 1부터 반복하여 List에 담긴 객체의 갯수만큼 증가한다.

 

응용(Lprod)

VO 내에서 회원에게 할당된 1 : N VO 연결을 했을 경우(Ex. 중첩된 자바빈),

DB 테이블을 두 개 사용하여 JOIN 문을 사용해야 한다.

 

우선 1:N의 쿼리문부터 작성해보자

--LPROD     : PRODUCT = 1 : N
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
             , B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER
             , B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
FROM     LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
WHERE  A.LPROD_GU = 'P101';

실행 결과

 

Controller의 detail 메소드 수정

@RequestMapping("/detail")
	public ModelAndView detail(ModelAndView mav, LprodVO lprodVO) {
		log.info("detail로 연결");
		
		// 상품 분류 목록
		lprodVO = this.lprodService.detail(lprodVO);
		log.info("lprodVO : " + lprodVO);
		
		// productVOList를 넣어보자
		 /*
	      P1014   iPhone 6s   800000
	      P1015   LG PC 그램   1500000
	      P1016   Galaxy Tab S   900000
         */
		List<ProductVO> productVOList = new ArrayList<ProductVO>();
		
		// 첫번째 상품
		ProductVO productVO1 = new ProductVO();
		productVO1.setProductId("P1014");
		productVO1.setPname("iPhone 6s");
		productVO1.setUnitPrice(800000);
		
		// 두번째 상품
		ProductVO productVO2 = new ProductVO();
		productVO2.setProductId("P1015");
		productVO2.setPname("LG PC 그램");
		productVO2.setUnitPrice(1500000);
		
		// 세번째 상품
		ProductVO productVO3 = new ProductVO();
		productVO3.setProductId("P1016");
		productVO3.setPname("Galaxy Tab S");
		productVO3.setUnitPrice(900000);
		
		productVOList.add(productVO1);
		productVOList.add(productVO2);
		productVOList.add(productVO3);
		
		lprodVO.setProductVOList(productVOList);
		
		log.info("lprodVO(상품 추가) : " + lprodVO);

		mav.addObject("lprodVO", lprodVO);
		
		mav.setViewName("lprod/detail");
		
		return mav;
	}

 

우선 하드코딩으로 직접 productVOList에 데이터를 넣어줬다.

<h1>상품 정보</h1>
<%-- <p>${lprodVO.productVOList}</p> --%>
<table border="1">
	<thead>
		<tr>
			<th>상품 아이디</th>
			<th>상품 명</th>
			<th>상품 가격</th>
		</tr>
	</thead>
	<tbody>
		<c:forEach var="product" items="${lprodVO.productVOList}" varStatus="stat">
			<tr>
				<td>${product.productId}</td>
				<td>${product.pname}</td>
				<td>${product.unitPrice}</td>
			</tr>
		</c:forEach>
	</tbody>
</table>

 

detail.jsp 하단부에 테이블을 하나 만들고 c:forEach를 통해 productVOList에서 객체를 하나씩 꺼내 product로 담아둔다.

담아둔 product에서 id, name, price값을 꺼내온다.

실행 결과

 

이제 위 작업을 쿼리문을 이용해 뽑아오도록 할 것

lprod_SQL.xml의 detail 부분을 위 작성해놨던 SQL 코드로 변경한다.

<!-- lprod : 1 -->
<resultMap type="lprodVO" id="lprodMap">
    <result property="lprodId" column="LPROD_ID"/>
    <result property="lprodGu" column="LPROD_GU"/>
    <result property="lprodNm" column="LPROD_NM"/>
    <collection property="productVOList" resultMap="productMap"></collection>
</resultMap>

<!-- product : N -->
<resultMap type="productVO" id="productMap">
    <result property="productId" column="PRODUCT_ID"/>
    <result property="pname" column="PNAME"/>
    <result property="unitPrice" column="UNIT_PRICE"/>
    <result property="description" column="DESCRIPTION"/>
    <result property="manufacturer" column="MANUFACTURER"/>
    <result property="category" column="CATEGORY"/>
    <result property="unitsInStock" column="UNITS_IN_STOCK"/>
    <result property="condition" column="CONDITION"/>
    <result property="filename" column="FILENAME"/>
    <result property="quantity" column="QUANTITY"/>
</resultMap>

<select id="detail" resultMap="lprodMap" parameterType="lprodVO">
    <!-- LPROD : PRODUCT = 1 : N -->
    SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
                 , B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER
                 , B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
    FROM     LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
    WHERE  A.LPROD_GU = #{lprodGu};
</select>

 

JOIN을 하는 순간 resultType -> resultMap으로 변경되어야 한다.

1 : N 의 경우 <collection> , 1 : 1 의 경우 <association> 을 사용하여 접근한다.

 

위와 같이 xml 코드를 작성해주고 아까 만든 컨트롤러 부분을 주석처리해주자.

실행 결과

 

 

스프링 폼 태크 라이브러리

HTML 폼을 출력하려면 <form : form> 요소를 사용한다.

 

registerForm.jsp를 만들어 준 다음 상단에 하단 코드를 넣어준다.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

라이브러리 적용

 

// 스프링 폼
// 2. 폼 요소
// 화면에 전달할 데이터를 위해 모델을 매개변수로 지정함
@GetMapping("/registerForm01")
public String registerForm01(Model model) {

   // 속성명에 "member"를 지정하고 폼 객체를 모델에 추가함
   // form:form modelAttribute="member"
   model.addAttribute("member",new Member());

   return "registerForm01";
}

 

<form:form>

<form:form modelAttribute="member" method="post" action="/registerForm01Post">

</form:form>

modelAttribute의 값과 controller에서 model에 넣은 속성명이 일치해야한다.

 

member 객체 생성할 때 기본값이 같이 담긴다.

 

<form:input>

<form:input path="userId" placeholder="아이디" />
<input type="text" name="userId" placeholder="아이디"/>

위, 아래 코드는 같은 구조를 가진다..

<form:input>의 path는 <input>의 name과 같다.

 

<form:button>

<form:button name="register">등록</form:button>
<input type="submit" value="등록" />

위 아래 코드는 같은 구조를 가진다. 위 코드가 border가 더 굵다

 

registerForm02

@GetMapping("/registerForm02")
   public String registerForm02(Model model) {
   
   //폼 객체의 속성명과 스프링 폼 태그의 
       //      modelAttribute 속성값이 일치하지 않으면
       //      에러 발생
   model.addAttribute("user",new Member());
   
   return "registerForm02";
   }

register02의 form:form태그

modelArrtibute의 값과 addAttribute로 넣은 속성명이 맞지 않아 발생하는 오류

 

modelAttribute의 값을 user로 바꿔주던가, addAttribute의 속성명을 member로 바꾸어줘야 한다.

 

registerForm03

// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
@GetMapping("/registerForm05")
public String registerForm05(Member member) {

   return "registerForm05";
}

실행 결과

 

model.addAttribute를 사용하지 않고 자바빈을 선언한 후 modelAttribute와 이름을 맞추면 오류없이 실행된다.

 

// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
// 2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의
// 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
// 3) ModelAttribute 애너테이션으로 폼 객체의 속성명을
//   직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(Member user) {

   return "registerForm05";
}

 

2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의 클래스명의 맨처음 문자를 소문자로 변환하여 처리

 

Member user와 modelAttribute의 값을 맞추지 않아도 클래스명(Member)를 소문자로 자동 변환하여 처리하기 때문에 오류나지 않는다.

 

그러나 거의 맞춰주는 편이 좋다.

 

// 컨트롤러 메서드의 매개변수로 자바빈즈 객체가
// 전달이 되면 기본적으로 다시 화면으로 전달
// 컨트롤러는 기본적으로 자바빈즈 규칙에 맞는 객체는
// 다시 화면으로 폼 객체를 전달함
// 2) 폼 객체의 속성명은 직접 지정하지 않으면 폼 객체의
// 클래스명의 맨처음 문자를 소문자로 변환하여 처리함
// 3) ModelAttribute 애너테이션으로 폼 객체의 속성명을
// 직접 지정할 수 있음
@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member user) {

   return "registerForm05";
}

 

3) ModelAttribute 애너테이션으로 폼 객체의 속성명을 직접 지정할 수 있음

 

@ ModelAttribute(" ") : " " 안의 이름은 modelAttribute와 맞춰주어야 한다.

 

 

폼 객체의 프로퍼티에 값을 지정

@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member member) {

   // 폼 객체의 프로퍼티에 값을 지정
   member.setUserId("gaeddong");
   member.setUserName("개똥이");

   return "registerForm05";
}

실행 결과

 

input:password

@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("member") Member member) {

   // 폼 객체의 프로퍼티에 값을 지정
   member.setUserId("gaeddong");
   member.setUserName("개똥이");
   member.setPassword("java");

   return "registerForm05";
}
 <p> 패스워드 : 
<!--       <input type="password" id="password" name="password"> -->
  <!-- 값을 설정해서 뷰에 전달하더라도 패스워드 필드에 반영안됨 -->
  <form:password path="password"/>

</p>

 

password는 적용되지 않는다.

 

 

input:textArea

@GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member) {

   // 폼 객체의 프로퍼티에 값을 지정
   member.setUserId("gaeddong");
   member.setUserName("개똥이");
   member.setPassword("java");
   member.setIntroduction("저는 개똥이입니다\n반갑습니다.");

   return "registerForm05";
}
<p> 소개글 : 
  <form:textarea path="introduction" rows="6" cols="30"/>
</p>

input:textarea 적용

 

 

Map타입의 데이터를 추가한 후 전달

 @GetMapping("/registerForm05")
public String registerForm05(@ModelAttribute("user") Member member) {

   // 폼 객체의 프로퍼티에 값을 지정
   member.setUserId("gaeddong");
   member.setUserName("개똥이");
   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("jmj","Book");
   
   member.setHobbyMap(hobbyMap);
   
   Map<String, String> carMap = new HashMap<String, String>();
   carMap.put("qm5", "qm5");
   carMap.put("sm6", "sm6");
   carMap.put("volvo", "volvo");

   member.setCarMap(carMap);

   return "registerForm05";
}
<p> 취미 : 
  <form:checkboxes items="${user.hobbyMap}" path="hobbyList"/>
</p>
<p> 보유 자동차 : 
  <form:checkboxes items="${user.carMap}" path="carList"/>
</p>

실행 결과

 

items = '${ modelAttribute값.map이름 }'의 형태로 작성해야한다.

 

 

체크박스에 체크되어있게 만들어주기

String[] hobbyArray = {"kmj"};
member.setHobbyArray(hobbyArray);

String[] carArray = {"volvo"};
member.setCarArray(carArray);
 <p> 취미 : 
  <form:checkboxes items="${user.hobbyMap}" path="hobbyArray"/> // path가 Array로 바뀜
</p>
<p> 보유 자동차 : 
  <form:checkboxes items="${user.carMap}" path="carArray"/> // path가 Array로 바뀜
</p>

실행 결과

 

<form:checkbox>

<!-- Controller -> String hobby -> kmj, jmj -->
<form:checkbox path="hobby" value="kmj" label="Music"/>&nbsp;
<form:checkbox path="hobby" value="ksy" label="Football"/>&nbsp;
<form:checkbox path="hobby" value="jmj" label="Book"/>&nbsp;
<p/>
<!-- Controller -> String[] hobbyArray -> ["kmj", "jmj"] -->
<form:checkbox path="hobbyArray" value="kmj" label="Music"/>&nbsp;
<form:checkbox path="hobbyArray" value="ksy" label="Football"/>&nbsp;
<form:checkbox path="hobbyArray" value="jmj" label="Book"/>&nbsp;

 

컨트롤러에 아래 코드 추가

member.setHobby("kmj");

실행 결과

private String[] hobbyArray;
private String hobby;

 

VO에 따라 path에 들어갈 값이 바뀌어야 한다.

 

 

<input:checkbox> - 2

 <p> 개발자 : 
    <form:checkbox path="developer" value="Y" 
    label=" Y/N" />
</p>
<p> 외국인 여부
    <form:checkbox path="foreigner" value="false"
     label=" true/false" />
</p>

실행 결과

 

개발자 : 체크하면 Y, 안하면 N

외국인 여부 : 체크하면 true, 안하면 false

 

 

<form:radiobuttons>

Map<String, String> genderCodeMap = new HashMap<String, String>();
genderCodeMap.put("Male","남성");
genderCodeMap.put("Female","여성");
genderCodeMap.put("etc","기타");

member.setGenderCodeMap(genderCodeMap);

// 미리 선택 처리
member.setGender("Female");
<p> 성별 : 
  <form:radiobuttons items="${user.genderCodeMap}" path="gender"/>
</p>

실행 결과

 

 

<form:radiobutton>

<form:radiobutton path="gender" value="Male" label="남성"/>&nbsp;
<form:radiobutton path="gender" value="Female" label="여성"/>&nbsp;
<form:radiobutton path="gender" value="etc" label="기타"/>&nbsp;

실행 결과

 

 

<form:select>

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");
<p> 국적 : 
  <form:select items="${user.notionalityCodeMap}" path="nationality"/>
</p>

실행 결과