1. 타임리프 스프링 통합
- 스프링 통합 메뉴얼 : Tutorial: Thymeleaf + Spring
Tutorial: Thymeleaf + Spring
Preface This tutorial explains how Thymeleaf can be integrated with the Spring Framework, especially (but not only) Spring MVC. Note that Thymeleaf has integrations for both versions 3.x and 4.x of the Spring Framework, provided by two separate libraries c
www.thymeleaf.org
- 타임리프는 스프링 없이도 동작하지만, 스프링 통합을 위한 다양한 기능제공.
* 스프링 통합으로 추가되는 기능들
- 스프링의 SpringEL 문법 통합
- ${@myBean.doSomething()} 처럼 스프링 빈 호출 지원
- 편리한 폼 관리를 위한 추가 속성 (th:object, th:field, th:errors, th:errorclss)
- 폼 컴포넌트 기능 (checkbox, radio, button, List 등을 편리하게 사용할 수 있는 기능 지원)
- 스프링의 메시지, 국제화 기능의 편리한 통합
- 스프링의 검증, 오류 처리 통합
- 스프링의 변환 서비스 통합(ConversionService)
* 설정방법
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
* 설정변경
Common Application Properties :: Spring Boot
Common Application Properties :: Spring Boot
docs.spring.io
2. 입력 폼 처리
- 폼의 입력, 수정에서 편해짐
* 입력
*.java
@GetMapping("/add")
public String addForm(Model model){
model.addAttribute("item", new Item()); //빈껍데기를 넘겨줌
return "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="formcontrol" 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="formcontrol" placeholder="수량을 입력하세요">
</div>
```
</form>
<!--
th:object="${item}" : <form> 에서 사용할 객체를 지정한다. 선택 변수 식( *{...} )을 적용할 수 있다.
th:field="*{itemName} : id="itemName" name="itemName" 을 대체해줌.
th:object 로 item을 선택하지 않은경우, ${item.itemName} 으로도 사용 가능.
-->
* 수정
*.java
@Slf4j
@Controller
@RequestMapping("/form/items")
@RequiredArgsConstructor
public class FormItemController {
private final ItemRepository itemRepository;
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model){
log.info("item.open={}", item.getOpen());
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "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" th:field="*{id}" class="form-control" readonly>
</div>
<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>
```
</form>
3. 요구사항 추가
/java/hello/itemservice/domain/item/itemType.enum
public enum ItemType {
BOOK("도서"), FOOD("식품"), ETC("기타");
private final String description;
ItemType(String description){
this.description = description;
}
public String getDescription() {
return description;
}
}
/java/hello/itemservice/domain/item/DeliveryCode.java
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* FAST : 빠른 배송
* NORMAL : 일반 배송
* SLOW : 느린 배송
**/
@Data
@AllArgsConstructor
public class DeliveryCode {
private String code;
private String displayName;
}
/java/hello/itemservice/domain/item/Item.java
import lombok.Data;
import java.util.List;
@Data
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
private Boolean open; //판매 여부
private List<String> regions; //등록 지역
private ItemType itemType; //상품 종류
private String deliveryCode; //배송 방식
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.price = price;
this.price = price;
this.quantity = quantity;
}
}
4. 체크 박스 - 단일
- 체크박스를 체크하면 HTML Form 에서 open=on 이라는 값이 넘어감 > 스프링은 on이라는 문자를 true 타입으로 변환
- 체크박스를 체크하지 않으면 open이라는 필드 자체가 서버로 전송되지 않음. (open 이라는 값자체가 오지 않음)
( 서버 구현에서 값이 오지 않은 것으로 판단해서 수정시에 값을 변경하지 않을 수 있음 > hidden 필드 사용 - 스프링제공)
/templates/form/addForm.html
<hr class="my-4">
<!-- 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>
* http 요청 메서드 출력
// application.properties
logging.level.org.apache.coyote.http11=trace
* 타임리프로 처리
- th:field 를 쓰면, 타임리프가 알아서 hidden _open을 만들어줌.
/templates/form/addForm.html
<hr class="my-4">
<!-- single checkbox -->
<div>판매 여부</div>
<div>
<div class="form-check">
<input type="checkbox" th:field="*{open}" class="form-check-input"> <!-- "${item.open}" -->
<label for="open" class="form-check-label">판매 오픈</label>
</div>
5. 체크박스 - 멀티
// 해당 컨트롤러가 호출될때 항상 model에 담기게됨.
@ModelAttribute("region")
public Map<String, String> regions(){
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
return regions;
}
@GetMapping("/add")
public String addForm(Model model){
model.addAttribute("item", new Item());
}
<!-- multi checkbox -->
<div>
<div>등록 지역</div>
<div th:each="region : ${regions}" class="form-check form-check-inline">
<!-- th:field="*{regions}" -->
<input type="checkbox" th:field="${item.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>
<!--
<div>
<div>등록 지역</div>
<div class="form-check form-check-inline">
<input type="checkbox" value="SEOUL" class="form-check-input" id="regions1" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="region1" class="form-check-label">서울</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" value="BUSAN" class="form-check-input" id="regions2" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="region2" class="form-check-label">부산</label>
</div>
<div class="form-check form-check-inline">
<input type="checkbox" value="JEJU" class="form-check-input" id="regions3" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="region3" class="form-check-label">제주</label>
</div>
</div>
-->
6. 라디오 버튼
@ModelAttribute("itemTypes")
public ItemType[] = itemTypes() {
return ItemType.values();
}
<!-- radio button -->
<div>
<div>상품 종류</div>
<!-- th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}" -->
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<!-- th:field="${item.itemType}" -->
<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>
</div>
</div>
<!--
<div>
<div>상품 종류</div>
<div class="form-check form-check-inline">
<input type="radio" value="BOOK" class="form-check-input" id="itemType1" name="itemType">
<label for="itemType1" class="form-check-label">도서</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" value="FOOD" class="form-check-input" id="itemType2" name="itemType">
<label for="itemType2" class="form-check-label">음식</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" value="ETC" class="form-check-input" id="itemType3" name="itemType">
<label for="itemType3" class="form-check-label">기타</label>
</div>
</div>
-->
7. 셀렉트 박스
@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;
}
<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>
References
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 (김영한)
'Thymeleaf' 카테고리의 다른 글
[01] 타임리프 - 기본기능 (0) | 2024.08.01 |
---|