타임리프는 스프링과 통합을 위한 다양한 기능을 편리하게 제공한다.
이번 글에서는 타임리프를 스프링에서 200% 활용할 수 있는 방법을 알아보자.
먼저 스프링 부트에서 타임리프를 사용하기 위해 build.gradle에 다음과 같이 등록해준다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf
기능
th:object
: 폼에서 컨트롤러에서 전달받은 특정 객체를 사용하겠다고 지정한다.*{...}
: th:object
에서 선택한 객체에 접근한다.th:field
: html태그의 id, name, value 속성을 자동으로 등록한다.이러한 기능을 통해 어떻게 폼을 편리하게 관리할 수 있는지 알아보자.
예를 들어 다음과 같이 Person이라는 클래스가 존재한다.
Person.java
public class Person(){
String name;
int age;
}
controller에서 뷰에 Person객체를 전달한다.
@GetMapping('/add')
public String addForm(Model model){
model.addAttribute("person",new Person());
return "form/addFrom";
}
addForm.html에서는 폼에서 Person객체를 사용하겠다고 지정할 수 있다.
<form action="person.html" th:action th:object="${person}" method="post">
<div>
<label for="personName">이름</label>
<input type="text" id="personName" th:field="*{personName}" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="age">나이</label>
<input type="text" id="age" th:field="*{age}" class="form-control"
placeholder="나이을 입력하세요">
</div>
장점
<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=on
이라는 값이 넘어간다. 스프링은 on을 true로 변환해준다.
만약 체크를 하지 않고 전송할 경우 open이라는 필드가 아예 서버로 전송되지 않는다.
이럴 경우 사용자가 체크되어 있던 값을 체크 해재해서 수정 저장시에도 서버에 아무 값이 넘어가지 않기 때문에 값을 변경하지 못한다.
이 문제를 해결하기 위해 _기존체크박스명
이라는 이름을 가지는 히든 필드를 만들어 기존 체크박스명이 전송되지 않고, _
가 붙은 필드가 전송되면 스프링은 체크를 해제했다고 판단한다.
체크박스에서 th:field
를 사용하게 되면 타임리프는 이러한 히든필드를 자동으로 생성해준다.
또한 체크박스의 값이 true인 경우 자동으로 checked
속성을 추가해준다.
멀티 체크박스 활용법
<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>
타임리프는 체크박스를 each루프 안에서 만들 때 임의로 필드명에 숫자를 붙여줌으로써 각 체크박스의 id를 고유하게 한다.
체크한 value들을 스프링이 배열로 받을 수 있다.
itemType
package hello.itemservice.domain.item;
public enum ItemType {
BOOK("도서"), FOOD("식품"), ETC("기타");
private final String description;
ItemType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
위의 itemType을 선택할 수 있는 폼을 만들어보자.
Controller
@ModelAttribute("itemTypes")
public ItemType[] itemTypes() {
return ItemType.values();
}
html
<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>
</div>
</div>
controller에서 셀렉트 박스에 출력할 값 전달
controller
@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;
}
html
<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>
form 생성시 이러한 타임리프 기능을 최대한 사용하도록 노력하여 더욱 간결하고 오류가 없는 프로그램을 개발하는 것이 좋을 것 같다.
※김영한님의 스프링 MVC2 강의를 듣고 정리한 내용입니다.