공부함
타임리프 스프링 통합, 폼 본문
타임리프는 스프링 없이도 동작하지만, 스프링 통합을 위한 다양한 기능을 제공한다.
- 스프링 EL 문법 통합
- 스프링 빈 호출 지원
- 편리한 폼 관리를 위한 추가 속성 지원
- 폼 컴포넌트 기능
- 메세지, 국제화 기능의 편리한 통합
- 검증, 오류 처리 통합
- 변환 서비스 ConversionService 통합
입력 폼 처리
렌더링 전
<input type="text" th:field="*{itemName}/>
렌더링 후
<input type="text" id="itemName" name="itemName" th:value="*{itemName}" />
th:object : 커맨드 객체 지정
*{ .. } : 선택 변수 식, th:object에서 지정한 객체에 접근
th:field : html 태그의 id, name,value 속성 값 자동 처리
<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>
form에서 사용할 객체를 th:object 속성으로 등록한다.
등록한 객체를 대상으로 *{ .. } 선택 변수 식을 사용할 수 있다.
th:field 는 위에서 말했듯이 id, field, value 속성값을 자동으로 만들어준다.
id, field 속성값은 th:field 에서 지정한 변수 이름과 같으며, value 속성값은 th:field에서 지정한 변수의 값을 사용한다.
단일 체크박스
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/form/items/{itemId}";
}
post form으로 (쿼리 스트링 형태로) 데이터가 넘어오면 그에 맞는 Item 객체가 @ModelAttribute에 의해 생성된다.
<!-- 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은 체크 해제를 인식하기 위한 히든 필드이다.
체크박스를 선택할 경우 open=on & _open = on이 된다. _open은 무시되고 item.open = true가 된다.
체크박스를 해제할 경우 _open만 있고 item.open = false가 된다.
위 방식은 MVC에서 제공하는 기능이다.
하지만 이렇게 매번 히든 필드를 추가하기는 번거롭다.
타임리프를 사용하면 더 간단하게 할 수 있다.
<div>판매 여부</div>
<div>
<div class="form-check">
<input type="checkbox" id="open" th:field="*{open}" class="form-checkinput">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
</div>
th:field = "${item.open}"으로 설정해주면 된다.
또는 th:object ="${item}"으로 변수를 선언하고 th:field = "*{open}" 선택 변수 식을 사용할 수도 있다.
멀티 체크 박스
@ModelAttribute("regions")
public Map<String, String> regions() {
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
return regions;
}
@ModelAttribute를 활용해 이러한 메서드를 컨트롤러 내에 선언해 주면 해당 컨트롤러의 메서드가 호출될 때는 항상 model의 regions가 담기게 된다.
중복을 해소하는데 유용하다.
<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:each 태그를 사용해서 반복문으로 멀티 체크박스를 렌더링한다.
주의 : th:field="*{regions}" 는 Item 객체의 regions이다.
<label>은 <input> 의 id를 알아야 한다. 그런데 위 예시에서는 th:field를 활용해서 id가 만들어지기 때문이 미리 알 수 가 없다. 이럴 때 th:for="${#ids.prev('regions')}"로 동적으로 생성되는 id 값을 만들 수 있다.
멀티 체크박스에서도 아무것도 선택하지 않으면 _regions=on&_regions=on&_regions=on과 같이 값이 넘어간다.
라디오 버튼
여러 선택지 중 하나 선택할 때 사용한다.
<!-- radio button -->
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio" th:field="${item.itemType}" th:value="${type.name()}" class="form-check-input" disabled>
<label th:for="${#ids.prev('itemType')}" th:text="${type.description}"
class="form-check-label">
BOOK
</label>
</div>
</div>
체크박스랑 거의 똑같다..
셀렉트 박스
마찬가지로 여러 선택지 중 하나 선택
<!-- 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>
<hr class="my-4">