${@myBean.doSomethin()}
처럼 스프링 빈 호출 지원타임리프를 템플릿 엔진으로 스프링 빈에 등록하려면, 타임리프 용 뷰 리졸버를 스프링 빈으로 등록해야 한다.
스프링 부트에서는 build.gradle에 타임리프 관련 의존성만 추가해주면 관련 설정을 모두 자동으로 스프링 빈으로 등록해준다.
implementation'org.springframework.boot:spring-boot-starter-thymeleaf`
그리고 대부분의 타임리프 관련 설정은 application.properties에 추가해 설정 및 변경이 가능하다.
타임리프 속성을 이용해 입력 폼을 효율적으로 개선할 수 있다.
th:obejct
: 커맨드 객체를 지정한다. *{...}
: 선택 변수 식으로 상위 태그의 th:object
에서 선택한 객체에 접근한다.th:field
: HTML 태그의 id
,name
,value
속성을 자동으로 처리해준다. <input type="text" th:field="*{itemName}"/>
<!-- 렌더링 후 --!>
<input type="text" id="itemName" name="itemName" value="*{itemValue}" />
폼에서 체크박스, 라디오 버튼, 셀렉트 박스를 타임리프를 이용하면 편리하게 사용할 수 있다.
HTML checkbox의 특징으로 check 가 되지 않는다면 클라이언트에서 서버로 값을 보내지 않는다.
<input type="checkbox" id="open" name="open" class="form-check-input" />
위와 같은 태그가 있다고 할때 내가 선택을 하지 않은 경우에 open이라는 프로퍼티가 false가 아니라 아예 전송이 되지 않으므로 null
상태이다.
( check를 하면 on
이라는 값이 넘어가는데 이를 스프링에서 true
로 변경해주며 해당 역할을 스프링 컨버터가 수행한다.)
이러한 특징은 수정의 경우 문제가 될 수 있다. 사용자가 의도적으로 체크를 하지 않고 저장을 할 경우 아무 값도 넘어가지 않기 때문에 서버 구현에 따라 값이 변경되지 않을 수 있다. (선택 -> 선택해제 )
스프링 MVC는 히든 필드를 이용해 이러한 문제를 해결한다.
기존 체크박스 태그 앞에 히든 필드를 하나 추가하고 _open
과 같이 언더스코어(_
)를 붙혀 전송하면 체크를 해제했음을 인식할 수 있다. 히든필드는 항상 전송된다는 점을 노린 방식인데, 체크를 해제한 경우 open
은 전송되지 않고 _open
만 전송되는데 이러한 경우를 스프링은 체크가 해제되었다고 판단한다.
<input type="hidden" name="_open" value="on" />
<input type="hidden" name="_open" value="on">
<input type="checkbox" id="open" name="open" class="form-check-input" />
이렇게 작성하면 체크박스를 선택하지 않아도 서버에서 null
값이 아니라 명확하게 false
를 받을 수 있다. 이는 스프링 MVC에서 _open만 존재하는 것을 확인 후 체크되지 않았음을 인식한다.
히든필드는 타임리프의 폼 기능을 사용해 자동으로 생성해줄 수 있다. th:field
속성을 이용하면 id,name,value,field뿐 아니라 hidden field와 checked속성까지 자동으로 설정해준다.
<input type="checkbox" th:field="*{open}" />
<input type="checkbox" id="open" name="open" th:value="true" checked="checked" />
<input type="hidden" name="_open" value="on">
체크 박스를 멀티로 사용하여 하나 이상을 나타나게 할 수도 있고 체크할수도 있게 할 수 있다.
@ModelAttribute의 또다른 사용법
: 보통 API 의 Request로 사용하는 이 어노테이션을 붙여주면 해당 메서드의 반환값이 자동으로 Model에 담기게 된다.
@ModelAttribute("data") public String data(){ return "testData"; }
-> data라는 이름으로 해당 속성을 사용할 수 있고 testData가 값으로 사용된다.
<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>
regions라는 반복 가능한 요소를 th:each
로 반복하도록 선언한다.
th:field
를 넣으면 th:value
값과 비교하여 값이 포함되면 checked가 추가된다. 즉, 자동으로 value와 비교해서 checked 여부를 설정할 수 있다.
th:for="${#ids.prev('regions')}"
-> 멀티 체크박스는 같은 이름의 여러 체크박스가 생성된다. name은 동일해도 되지만 id는 모두 달라야 하므로 타임리프의 편의 목적으로 ids라는 프로퍼티를 제공하며 아래와 같은 메서드를 제공한다. ids는 반복하는 요소의 인덱스로 1,2,3처럼 숫자다.
-> ids.prev(...) : 아이디 앞에 인수값을 문자열 결합해준다. ( Ex : ids.prev('data') - data1
-> ids.next(...) : 아이디 뒤에 인수값을 문자열 결합해준다. ( Ex : ids.next('data') - 1data
다중 선택이 안되는 라디오 버튼은 선택지 중 하나를 선택할 수 있다.
<div th:obejct="${item}">
...
<div th:each="type : ${itemTypes}" class="form-check form-check- inline">
<input type="radio" th:field="*{itemTypes}" th:value="${type.name()}"class="form-check-input">
<label th:for="{#ids.prev('itemTypes')}" th:text="${type.description}" class="form-check-label">
BOOK
</label>
</div>
</div>
라디오 버튼과 비슷하게 여러 선택지 중 하나를 선택할 때 사용할 수 있다.
<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>
-> select 태그에 선언된 th:field속성과 option에서 선언된 th:value를 타임리프가 비교해 일치할 경우 자동으로 selected 된다.