스프링 부트 스타터 사이트로 이동해서 프로젝트 생성
✔️ 프로젝트 선택
✔️ Project Metadata
✔️ Dependencies
📌 타임리프는 크게 2가지 메뉴얼을 제공한다
1️⃣ 기본 메뉴얼
2️⃣ 스프링 통합 메뉴얼
타임리프는 스프링 없이도 동작하지만, 스프링과 통합을 위한 다양한 기능을 편리하게 제공한다!
타임리프 관련 설정을 변경하고 싶다면 ⬇️아래의 링크⬇️를 참고해서 application.properties
에 추가하면 된다.
지금부터 타임리프가 제공하는 입력 폼 기능을 적용해서 기존 프로젝트의 폼 코드를 타임리프가 지원하는 기능을 사용해서 효율적으로 개선해보자❗️
✔️ FormItemController
변경
th:object
를 적용하려면 먼저 해당 오브젝트 정보를 넘겨주어야 한다.✔️ form/addForm.html
변경
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
</div>
th:object="${item}"
: <form>
에서 사용할 객체를 지정한다. 선택 변수 식(*{...})
을 적용할 수 있다.th:field="*{itemName}"
*{itemName}
는 선택 변수 식을 사용했는데, ${item.itemName}
과 같다. th:object
로 item을 선택했기 때문에 선택 변수 식을 적용할 수 있다.th:field
는 id
, name
, value
속성을 모두 자동으로 만들어준다.id
: th:field
에서 지정한 변수 이름과 같다. id="itemName"
name
: th:field
에서 지정한 변수 이름과 같다. name="itemName"
value
: th:field
에서 지정한 변수의 값을 사용한다. value=""
📌 참고
해당 예제에서id
속성을 제거해도th:field
가 자동으로 만들어준다.
✔️ form/editForm.html
변경
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="id">상품 ID</label>
<input type="text" id="id" class="form-control" th:field="*{id}" readonly>
</div>
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" class="form-control" th:field="*{itemName}" >
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" class="form-control" th:field="*{price}">
</div>
<div>
id
, name
, value
를 모두 신경써야 했는데, 많은 부분이 th:field
덕분에 자동으로 처리되는 것을 확인할 수 있다👍🏻타임리프를 사용해서 폼에서 체크박스, 라디오 버튼, 셀렉트 박스를 편리하게 사용하는 방법을 학습해보자!
기존 상품 서비스에 다음 요구사항이 추가되었다 😊
✔️ ItemType - 상품 종류
✔️ 배송 방식 - DeliveryCode
✔️ Item - 상품
⬆️ 3개의 파일을 생성한 후 작업을 시작해보자!
✔️ 단순 HTML 체크박스
<!-- single checkbox -->
<div>판매 여부</div>
<div>
<div class="form-check">
<input type="checkbox" id="open" name="open" class="form-check-input">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
</div>
open
은 전송되지 않고, _open
만 전송되는데, 이 경우 스프링 MVC는 체크를 해제했다고 판단한다!!체크 해제를 인식하기 위한 히든 필드
<input type="hidden" name="_open" value="on"/>
✔️ 기존 코드에 히든 필드 추가
<!-- single checkbox -->
<div>판매 여부</div>
<div>
<div class="form-check">
<input type="checkbox" id="open" name="open" class="form-check-input">
<input type="hidden" name="_open" value="on"/> <!-- 히든 필드 추가 -->
<label for="open" class="form-check-label">판매 오픈</label>
</div>
</div>
✔️ 실행 로그
- 체크 박스 체크 : open=on&_open=on
_open=on
🤔 : 개발할 때 마다 이렇게 히든 필드를 추가해야 하는 거야? 귀찮은데,,
😉 : 타임리프가 제공하는 폼 기능을 사용하면 이런 부분을 자동으로 처리할 수 있단다^^
✔️ 타임리프 - 체크박스 코드 추가
<!-- single checkbox -->
<div>판매 여부</div>
<div>
<div class="form-check">
<input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
</div>
⬆️ 타임리프 체크박스 HTML 생성 결과
<input type="hidden" name="_open" value="on"/>
위의 내용을 상품 상세 와 상품 수정 에도 똑같이 적용하자 👍🏻
체크 박스를 멀티로 사용해서, 하나 이상을 체크할 수 있도록 해보자! 예) 등록 지역
✔️ FormItemController
- 추가
@ModelAttribute("regions")
public Map<String, String> regions() {
Map<String, String> regions = new LinkedHashMap<>(); regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
return regions;
}
model.addAttribute(...)
을 사용해서 체크 박스를 구성하는 데이터를 반복해서 넣어주어야 한다.✔️ addForm.html
- 추가
<!-- multi checkbox -->
<div>
<div>등록 지역</div>
<div th:each="region : ${regions}" class="form-check form-check-inline">
<input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input">
<label th:for="${#ids.prev('regions')}"
th:text="${region.value}" class="form-check-label">서울</label>
</div>
</div>
th:for="${#ids.prev('regions')}"
✔️ each로 체크박스가 반복 생성된 결과 - id 뒤에 숫자가 추가
<input type="checkbox" value="SEOUL" class="form-check-input" id="regions1"
name="regions">
<input type="checkbox" value="BUSAN" class="form-check-input" id="regions2"
name="regions">
<input type="checkbox" value="JEJU" class="form-check-input" id="regions3"
name="regions">
id
가 타임리프에 의해 동적으로 만들어지기 때문에 <label for="id 값">
으로 label 의 대상이 되는 id
값을 임의로 지정하는 것은 곤란하다🤔 ids.prev(...)
, ids.next(...)
을 제공해서 동적으로 생성되는 id 값을 사용할 수 있도록 한다❗️💡 타임리프에서 name은 겹쳐도 되지만, id는 겹쳐서는 안 된다!!
⬆️ 상품 등록 폼에서 서울, 부산을 선택하고 확인한 로그 결과👍🏻
만약 지역선택을 하지 않는다면 어떻게 될까😐❓
_regions=on&_regions=on&_regions=on
➡️ 앞서 배웠듯, 웹 브라우저에서 체크를 하나도 하지 않았을 때, 클라이언트가 서버에 아무런 데이터를 보내지 않는 것을 방지한다!
✔️ item.html
에도 멀티 체크박스 부분 추가
item.html
에는 th:object
를 사용하지 않았기 때문에 th:field
부분에 ${item.regions}
으로 적어주어야 한다.✔️ editForm.html
에도 멀티 체크박스 부분 추가
라디오 버튼은 여러 선택지 중에 하나를 선택할 때 사용할 수 있다! 라디오 버튼을 ❗️자바 ENUM을 활용해서❗️ 개발해보자🙂 예) 상품 종류
(체크박스는 여러개 선택 가능)
✔️ FormItemController
- 추가
@ModelAttribute("itemTypes")
public ItemType[] itemTypes() {
return ItemType.values();
}
itemTypes
를 등록 폼, 조회, 수정 폼에서 모두 사용하므로 @ModelAttribute
의 특별한 사용법을 적용하자.ItemType.values()
를 사용하면 해당 ENUM의 모든 정보를 배열로 반환한다.✔️ addForm.html
- 추가
<!-- radio button -->
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input">
<label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label">
BOOK
</label>
</di
💡 라디오 버튼은 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다!
✔️ item.html
에도 라디오 버튼 부분 추가
item.html
에는 th:object
를 사용하지 않았기 때문에 th:field
부분에 ${item.itemType}
으로 적어주어야 한다.✔️ editForm.html
에도 라디오 버튼 부분 추가
💡 타임리프에서 ENUM 직접 사용하기
<div th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}">
➡️ 스프링EL 문법을 사용하여 모델에 굳이 담지 않고도 직접 사용이 가능하지만, 이 방법을 추천하지는 않는다!
셀렉트 박스는 여러 선택지 중에 하나를 선택할 때(❗️하나만 선택해야 함❗️) 사용할 수 있다! 예) 배송 방식
✔️ FormItemController
- 추가
@ModelAttribute("deliveryCodes")
public List<DeliveryCode> deliveryCodes() {
List<DeliveryCode> deliveryCodes = new ArrayList<>();
deliveryCodes.add(new DeliveryCode("FAST", "빠른 배송"));
deliveryCodes.add(new DeliveryCode("NORMAL", "일반 배송"));
deliveryCodes.add(new DeliveryCode("SLOW", "느린 배송"));
return deliveryCodes;
}
DeliveryCode
라는 자바 객체를 사용DeliveryCode
를 등록 폼, 조회, 수정 폼에서 모두 사용하므로 여기서도 @ModelAttribute
사용!✔️ addForm.html
- 추가
<!-- SELECT -->
<div>
<div>배송 방식</div>
<select th:field="*{deliveryCode}" class="form-select">
<option value="">==배송 방식 선택==</option>
<option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}"
th:text="${deliveryCode.displayName}">FAST</option>
</select>
</div>
✔️ item.html
에도 셀렉트 박스 부분 추가
item.html
에는 th:object
를 사용하지 않았기 때문에 th:field
부분에 ${item.deliveryCode}
으로 적어주어야 한다.✔️ editForm.html
에도 셀렉트 박스 부분 추가
⬆️ 총 실행 결과 구우우우웃 👍🏻