tymeleaf 스프링 통합과 폼 정리

2023. 6. 29. 19:42Spring

728x90

타임리프 스프링 통합

타임리프는 크게 2가지 메뉴얼을 제공한다.

 

Tutorial: Using Thymeleaf

1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide a

www.thymeleaf.org

 

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:errorclass
  • 폼 컴포넌트 기능
    • checkbox, radio, button, List등을 편리하게 사용할 수 있는 기능 지원
  • 스프링 메시지, 국제화 기능의 편리한 통합
  • 스프링 검증, 오류 처리 통합
  • 스프링의 변환 서비스 통합(ConversionService)

설정 방법

타임리프 템플릿 엔진을 스프링 빈에 등록, 타임리프용 뷰 리졸버를 스프링 빈 등록 방법

https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#the-springstandard-dialect

 

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

https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#views-and-view-resolvers

 

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

스프링 부트는 이런 부분을 모두 자동화 해준다. build.gradle에 아래의 코드를 추가해주면 Gradle은 타임리프와 관련된 라이브러리를 다운 받고, 스프링 부트는 위 메뉴얼에 있는 타임리프와 관련된 설정용 스프링 빈을 자동으로 등록해준다.

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

만약 타임리프 관련 설정을 변경하고 싶으면 아래의 메뉴얼을 참고해서 추가해주면 됨.

https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties.templating

 

Common Application Properties

 

docs.spring.io

 

입력 폼 처리

타임리프가 제공하는 입력 폼 기능

  • th:object : 커맨드 객체를 지정한다.
  • *{...} : 선택 변수 식이라고 한다. th:object에서 선택한 객체에 접근함.
  • th:field : HTML 태그의 id, name, value 속성을 자동으로 처리해줌.

ex.)

랜더링 전

<input type="text" th:field="*{itemName}" />

랜더링 후

<input type="text" id="itemName" name="itemName" th:value="*{itemName}" />

th:object를 적용하려면 해당 오브젝트 정보를 넘겨줘야 한다. 

ex.) 상품 등록 폼

<!-- th:object="${item}" : <form>에서 사용할 객체 지정. 선택 변수 식(*{...})적용 가능 -->
<form action="item.html" th:action th:object="${item}" method="post">
    <div>
        <label for="itemName">상품명</label>
        <!-- 여기서 *{itemName} 선택 변수 식 사용했는데 ${item.itemName}이랑 똑같음 -->
        <!-- 위에서 th:object로 item을 선택했기 때문에 선택 변수 식 적용 가능함. -->
        <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
    </div>
</form>
  • th:field는 id, name, value 속성을 모두 자동으로 만들어줌.
    • id : th:field에서 지정한 변수 이름과 같음 ex.) id = "itemName"
    • name : th:field에서 지정한 변수 이름과 같음. ex.) name="itemName"
    • value : th:field에서 지정한 변수의 값 사용. ex.) value=""

참고로 위에 코드에서 id 속성 제거해도 th:field가 자동으로 만들어줌.

ex.)

렌더링 전

<input type="text" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">

렌더링 후

<input type="text" class="form-control" placeholder="이름을 입력하세요" id="itemName" name="itemName" value="">

 

ex.) 상품 수정 폼

상품 등록 폼 보다는 기존 데이터를 불러오기 위해 id, name, value를 모두 신경써서 작성해야 하는 수정 폼에서 타임리프 입력 폼을 사용해보면 기존 타임리프 작성 폼을 사용 안한것 보다 편리한 것을 확인할 수 있다.

렌더링 전

<input type="text" th:field="*{itemName}" class="form-control">

렌더링 후

<input type="text" class="form-control" id="itemName" name="itemName" value="itemA">

타임리프 입력 폼을 사용하면 id, name를 잘못 입력 할 경우 페이지에서 바로 에러를 발생시켜줌.

 

단일 체크 박스1

ex.) 

<div class="form-check">
    <input type="checkbox" id="open" name="open" class="form-check-input">
    <label for="open" class="form-check-label">판매 오픈</label>
</div>
  • 체크 박스 선택시 : true
    • 체크 박스를 체크하면 HTML Form에서 open=on 이라는 값이 넘어간다. 스프링은 on 이라는 문자를 true 타입으로 변환해줌.
  • 체크 박스 선택X : null
    • HTML에서 체크 박스를 선택하지 않고 폼을 전송하면 open이라는 필드 자체가 서버로 전송되지 않음.

HTML checkbox는 선택이 안되면 클라이언트에서 서버로 값 자체를 보내지 않는다. 수정의 경우 상황에 따라서 이 방식이 문제가 될 수 있음. 사용자가 의도적으로 체크되어 있던 값을 해제해도 저장시 아무 값도 안넘어가기 때문에 서버 구현에 따라서 값이 오지 않은 것으로 판단해서 값이 변경되지 않을 수 있음.

 

위 문제를 해결하기 위해 스프링 MVC는 약간의 트릭을 사용하는데, 히든 필드를 하나 만들어서 _open처럼 기존 체크 박스 이름 앞에 _를 붙여 전송하면 체크를 해제했다고 인식가능.

ex.) <input type="hidden" name="_open" value="on"/>

  • 체크 박스 선택시 : true
    • 체크 박스를 체크하면 open=on&_open=on값을 넘기고 스프링 MVC가 open에 값이 있는 것을 확인하고 사용함. 이때 _open은 무시.
  • 체크 박스 선택X : false
    • 체크 박스를 체크하지 않으면 _open=on값을 넘기고 스프링 MVC가 _open만 있는 것을 확인하고 open에 값이 체크되지 않았다고 인식함.

하지만 위 방식을 사용하면 항상 히든 필드를 추가해야하기 때문에 번거롭다. 타임리프가 제공하는 폼기능을 사용하면 이런 부분을 자동으로 처리할 수 있음.(아래 단일 체크 박스2에서 설명)

 

 단일 체크 박스2

체크 박스의 기존 코드 제거하고 타임리프가 제공하는 체크 박스 코드로 변경.

ex.)

<!-- 변경 전 -->
<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 class="form-check">
	<!-- th:field 사용 -->
    <input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
    <label for="open" class="form-check-label">판매 오픈</label>
</div>

타임리프를 사용하면 체크 박스의 히든 필드와 관련된 부분도 함께 해결해줌. HTML 생성 결과를 보면 아래의 결과처럼 히든 필드 부분이 자동으로 생성되어 있음.

<input type="checkbox" id="open" class="form-check-input" name="open" value="true"><input type="hidden" name="_open" value="on"/>

 

타임리프의 체크 확인

th:field를 사용하면 값이 true인 경우 checked="checked"를 자동으로 처리해주고, false면 checked속성 추가 안함.

 

체크 박스 멀티

체크 박스를 멀티로 사용해서, 하나 이상을 체크할 수 있도록 설정해보자.

ex.)

<!-- multi checkbox -->
<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>
  • th:for="${#ids.perv('regions')}"
    • 멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다. 그런데 문제는 이렇게 반복해서 HTML태그를 생성할 때, 생성된 HTML태그 속성에서 name은 같아도 되는데 id는 모두 달라야 한다. 타임리프는 체크박스를 each 루프 안에서 반복해서 만들 때는 임의로 1, 2, 3 숫자를 뒤에 붙여준다.
    • <input type="checkbox" value="SEOUL" class="form-check-input" id="regions1" name="regions">

HTML의 id가 타임리프에 의해 동적으로 만들어지기 때문에 <label for=id 값">으로 label의 대상이 되는 id값을 임의로 지정하는 것은 곤란하다. 타임리프는 ids.prev(...), ids.next(...)을 제공해서 동적으로 생성되는 id 값을 사용할 수 있도록 해준다.

이 부분도 위에서 설명한 히든필드, checked를 자동으로 설정해준다.

 

라디오 버튼

ex.)

<!-- radio button -->
<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>

라디오 버튼은 값이 없을 때 null값을 반환해준다.

체크 박스는 수정시 체크를 해제하면 아무 값도 넘어가지 않기 때문에, 별도의 히든 필드로 이런 문제를 해결했는데, 라디오 버튼은 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없음.

 

타임리프에서 ENUM 직접 사용하기

Controller에서 ENUM을 담아서 전달하지 않아도 타임리프는 스프링EL 문법으로 자바 객체에 직접 접근할 수 있다.

ex.)

<div th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}">

그런데 이렇게 사용하면 ENUM의 패키지 위치가 변경되거나 할 때 자바 컴파일러가 타임리프까지 컴파일 오류를 잡지 못하므로 추천하지 않는 방법이다.

 

셀렉트 박스

ex.)

<!-- SELECT -->
<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>

<option value="FAST" selected="selected">빠른 배송</option>

배송 방식을 빠른 배송을 선택했다면 위에 보이는식으로 selected="selected"로 선택된 샐렉트 박스가 유지되는 것을 확인할 수 있다.

 

728x90