Spring

Thymeleaf와 Spring

민철킹 2021. 7. 4. 17:19

기본 메뉴얼 : https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

 

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

 

스프링 통합 메뉴얼 : https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html

 

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

 

 

더보기

사실 게시판 토이프로젝트를 진행하면서 다 사용해본 기능들이지만 한번 복습한다는 생각으로 정리를 하였다.

Thymeleaf가 Spring을 위해 제공되는 기능

- 스프링의 SpringEL 문법 통합

- ${@myBean.메서드()} 처럼 스프링 빈 호출 지원

- 편리한 폼 관리를 위한 추가 속성(th:object, th:field, th:errors, th:errorclass)

- 폼 컴포넌트 기능(checkbox, radio button, List 등을 편리하게 사용 가능)

- 스프링의 메시지, 국제화 기능의 편리한 통합

- 스프링의 검증, 오류 처리 통합

- 스프링의 변환 서비스 통합(ConversionService)

 

스프링 부트는 build.gradle에 타임리프 라이브러리를 넣어주면 자동으로 라이브러리 다운과 설정용 스프링 빈을 자동으로 등록해줌.

 

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

 


1. 입력 폼 처리

 

- th:object  : 커맨드 객체를 지정

- *{} : 선택 변수 식, th:object에서 선택한 객체에 접근

- th:field : HTML 태그의 id, name, value 속성을 자동으로 처리

 

상품 추가하는 애플리케이션이 존재한다. Get으로 상품 추가 폼을 요청하고 Post로 Form에 상품 정보를 담아 전송하면 Repository에 저장되는 방식으로 동작한다.

모델에 빈 Item 객체 담아서 넘겨줌.

th:object와 th:field를 사용하기 위해 빈 객체를 하나 넘겨준다.

th:field는 name, id, value를 자동으로 생성해준다. 따라서 Form 데이터를 전송할 때 원하는 객체에 맞춰 값을 담을 수 있다.

즉 *{itemName}은 ${item.itemName}과 같다. 커맨드 객체로 item을 선택해놨기 때문에 *{itemName}과 같이 사용이 가능하다.

 

이렇게 form 데이터를 전송하면 컨트롤러에서 @ModelAttribute를 통해 상품 객체에 값을 담아 저장하는 것이다.

내가 만든 게시판 프로젝트에서도 이 방법을 사용해 새로운 게시물, 새로운 회원을 등록시킨다.

 

 


2. 체크 박스

 

HTML checkbox는 선택이 되지 않으면 클라이언트에서 서버로 값 자체를 보내지 않는다. 수정의 경우에는 상황에 따라 문제가 발생할 수 있다. 사용자가 체크되어 있던 값을 해제하더라도 아무 값도 넘어가지 않기 때문에, 서버 구현에 따라서 값이 오지 않는 것으로 판단해 값을 변경하지 않을 수도...

 

체크를 하지 않으면 값 자체가 넘어가지 않아 item 객체의 해당 필드에 null값이 들어감.

 

이를 해결하기 위해 스프링 MVC는 히든 필드를 하나 만들어 기존 checkbox이름 앞에 _를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다. 히든 필드는 항상 전송되기 때문에 체크가 해제된 경우 체크 박스 값은 전송되지 않고 히든 필드만 전송되기 때문에 스프링MVC는 체크가 해제되었다고 판단한다.

 

동일하게 새로운 상품을 등록하는데 체크박스를 해제하고 값을 넘기면 위와 같이 히든 필드가 전송된다.

를 보고 스프링 MVC는 체크가 해제되었다고 판단함.

컨트롤러에서 open의 값을 로그로 찍어보면,

히든 필드를 사용하기 전에는 null이 담겼지만 이제는 의도대로 false가 들어간다.

 

하지만 이것은 스프링 MVC가 제공하는 기능이고, 매번 히든 필드를 추가하는 것 또한 번거롭다.

이를 위해 타임리프가 제공하는 기능이 존재한다.

 

타임리프

타임리프를 사용하면 위에서 언급한 히든필드를 추가하는 부분을 자동으로 처리할 수 있다.

마찬가지로 th:field를 사용하면 된다.

자동으로 히든 필드가 추가된 것을 확인할 수 있다.

타임리프의 th:field 를 사용하면, 값이 true 인 경우 체크를 자동으로 처리해준다.

 

 

멀티 체크박스

요구사항

상품에 regions라는 필드를 추가한다. 이 필드는 해당 상품이 어디에서 판매되는지를 나타내는 속성이다.

상품을 등록하고 수정할 때 멀티 체크박스로 판매될 지역을 선택하여 저장한다. 또한 상품을 조회할 때도 해당 상품이 어떤 지역에서 판매되는가를 보여주어야한다.

 

먼저 해야할 일은 체크박스에 표시될 지역의 목록을 model에 담아 넘겨주어야한다.

LinkedHashMap으로 Map을 만들어 모델에 담고 뷰로 넘겨준다. 이 코드를 수정, 조회, 등록 컨트롤러에 동일하게 추가시켜주어야한다. 하지만 동일한 코드를 반복해서 사용하는 것은 좋지 않은 방식이다. 이를 해결하기 위해 스프링MVC에서 제공하는 방법이 있다.

@ModelAttribute를 위와 같이 사용하면 해당 클래스안에 있는 모든 컨트롤러가 호출될 때 내부의 메서드가 실행되어 모델에 값이 담긴다.

Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
model.addAttribute("regions", regions);

즉 모든 컨트롤러 내부에 이 코드가 추가된 것과 같이 동작하는 것이다.

무조건 이 방법을 사용하는 것이 좋은게 아니라 모델에 데이터를 담는 동일한 코드가 반복될 때 효과적으로 사용될 수 있는 방법이다. 물론 각 컨트롤러에 이전과 같이 직접 모델에 값을 담아도 동일하게 동작함.

 

Thymeleaf의 each를 통해 Map에 저장된 값들을 하나씩 꺼내온다. 이 때 input과 label을 연결하기 위해 input의 id값을 가져오는데 이 때 사용하는 것이 Thymeleaf의 #ids 객체이다. HTMl 태그 속성에서 name은 같아도 되지만 id는 모두 달라야함.(Thymeleaf는 each 루프 안에서 반복될 때 임의로 id뒤에 하나씩 증가하는 숫자를 붙혀준다.(1,2,3......)

 

상품 조회는 id를 통해 해당 상품을 찾아 model에 담아 view로 넘기는 방식으로 구현되어 있다.

새로운 상품을 서울, 부산 지역에만 등록하고 조회를 해보면 설정한대로 checkbox가 체크되어 있는 것을 확인할 수 있는데 이는 Thymeleaf가 th:field와 th:value를 비교하여 값이 있으면 자동으로 checked를 추가해주기 때문이다.

==> 조회된 상품의 regions 필드 : [서울, 부산], th:value에는 each를 통해 값이 하나씩 들어감.

==> 서울이 regions 필드에 존재하면 checked를 추가해주는 방식으로 동작

 

 

 


3. 라디오 버튼

 

Enum타입으로 상품의 종류를 설정해두고 상품을 등록할 때 이 중에서 선택할 수 있도록 구현해보자.

 

마찬가지로 @ModelAttribute를 필드에 사용하여 모든 컨트롤러에 Enum타입을 배열에 담아 Model에 넣어준다.

 

Thymeleaf를 사용하여 라디오박스를 생성.

이와 같이 라디오 버튼가 생성되었다.(라디오 버튼은 하나만 선택할 수 있음)

마찬가지로 상품 수정, 조회 form에도 라디오 박스를 넣어준다.

 

라디오 버튼은 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다.

 

타임리프에서 ENUM에 직접 접근하기

위에서 사용한 방법은 모델에 ENUM을 담아서 VIEW로 전달하였다.

타임리프에서는 스프링EL문법을 사용하여 ENUM에 직접 접근할 수 있다.

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

하지만 ENUM에 직접 접근하는 방법은 ENUM 클래스의 패키지 위치가 변경되면 컴파일러가 컴파일 오류를 잡을 수 없다는 단점이 존재한다.(컴파일 오류는 항상 런타임 오류보다 좋다.)

 

 


4. 셀렉트 박스

 

이번에는 ENUM이 아닌 자바 객체를 사용하여 셀렉트 박스를 만든다. 마찬가지로 @ModelAttribute를 통해 모든 컨트롤러에 담아준다.(사실 위와 같이 자바 객체를 사용하는 방식에는 @ModelAttribute를 사용하면 컨트롤러가 호출될 때마다 객체 인스턴스가 계속 새로 생성되기 때문에, 미리 생성해두고 재사용하는 것이 더 효율적이다. 하지만 공부니까^^)

 

셀렉트 박스를 생성하였다. 게시판 토이프로젝트에서 게시물의 카테고리를 선택할 때 이 방식을 적용시켜 만들었었음.

 

 

조회, 수정에도 동일한 방식으로 추가(조회에는 disabled)

 

조회, 수정시에  Thymeleaf가 th:field와 th:value를 비교하여 선택된 값이 무엇인지 판단하여 selected="selected"를 넣어줌.

 

 

 

반응형