카테고리 없음

타임리프(ThymeLeaf ) - 2

leegeonwoo 2024. 4. 23. 10:55

 

 

타임리프(ThymeLeaf )

타임리프란 타임리프는 서버 사이드 렌더링을 지원하는 뷰 템플릿의 종류 중 하나로 이외에 뷰 템플릿으로는 JSP등이 있다. 타임리프의 가장 큰 특징으로는 순수 HTML을 최대한 유지한다는 점이

lee-dev-log.tistory.com

저번 포스팅에서 다뤘던 내용들을 활용하며 상품관리 애플리케이션에 적용해보며 Spring프레임워크에서 지원하는 Thymeleaf기능을 사용해보자


Spring의 form태그 편리성 지원

Spring은 Thymeleaf를 사용할 때 HTML태그의 id, name, value속성 설정을 편리하게하도록 지원해주는 기능을 지원한다.

<form th:action>
    <input type="text" th:field="${item.itemName}">
    <input type="text" th:field="${item.quantity}">
</form>

위와 같이 코드를 설정하면 HTML태그안에 직접 id, name, value값을 설정하지 않아도 Thymeleaf가 HTTP 요청으로부터 받은 모델에 담겨있는 값으로 알아서 설정해준다.

 

실행결과

<form action="">
    <input type="text" id="itemName" name="itemName" value="itemA">
    <input type="text" id="quantity" name="quantity" value="1">
</form>

 

선택변수 식을 사용하여 아래와 같이 좀 더 간결하게 사용할 수 있다.

<form th:action th:object="${item}">
    <input type="text" th:field="*{itemName}">
    <input type="text" th:field="*{quantity}">
</form>

 

 

 

Spring의 checkbox속성 지원

순수 HTML에서 input태그의 checkbox속성을 사용할 때 문제점이 하나있는데 아래 코드의 실행결과를 살펴보자

    @GetMapping("test-add")
    public String addFormTest(@ModelAttribute Item item, Model model) {
        log.info("item.open={}", item.getOpen());
        model.addAttribute("item", new Item("itemA", 100, 1));
        return "form/formTest";
    }
<form th:action>
    <input type="checkbox" id="open" name="open">
    <label for="open">판매 오픈</label>

    <button type="submit">전송</button>
</form>

 

 

위와 같이 코딩을 한 뒤 open의 체크박스가 활성화, 비활성화된 상태를 각각 서버에 전송하면

위와 같은 실행결과를 서버에서 받을 수 있는데 checkbox속성에 대해 활성화 되어있을 때는 true, 비활성화 되어있을 때는 null값을 받는다.

이 때 비활성화된 checkbox를 null로 넘어가게되는데 이 경우에는 아래와 같은 문제점들을 야기할 수 있다.

1. 상태 변경의 어려움과 데이터 불일치
만약 사용자가 open이라는 데이터의 체크박스를 활성화시켜놓은 상태에서 후에 비활성화 상태로 변경하고 싶을 때 서버는 비활성화 상태를 인지할 수 없기때문에 변경을 처리할 수 없게됩니다.
2. 서버에서 데이터처리의 복잡성 증가
만약 false값이 들어온다면 데이터를 처리할 때 false값에 대해서 처리하면되는데 null로 들어오게 될 경우 따로 null값에 대해서 기본값을 설정하는 처리가 필요할수 있게됩니다.

 

이 문제를 해결하기위해서는 약간의 꼼수가 사용되는데

바로 'hidden' 속성을 사용하는 것이다.

<form th:action>
    <input type="checkbox" id="open" name="open">
    <input type="hidden" name="_open" value="on">
    <label for="open">판매 오픈</label>

    <button type="submit">전송</button>
</form>

type="hidden"은 브라우저에서는 보여지지 않는 태그이며 name속성의 값을 {_ + [필드이름]} 으로 설정해주면  서버에서도 null값을 false로 인지할 수 있다.

 

_open값은 숨겨져있는 값이기때문에 서버에 항상 전송된다.

만약 _open의 값만 전송되었을 경우 서버에서는 open의 값이 체크되지 않았다는 것을 인식하고 false값으로 처리하게 된다.  

실행결과

 

하지만 checkbox속성을 사용할때 마다 hidden필드를 넣어주는 것은 매우 귀찮고 코드의 가독성이 떨어질수 있기때문에 이전에 다룬 thymeleaf의 th:field를 사용하면 이런 번거로움도 해결할 수 있다.

<form th:action>
    <input type="checkbox" th:field="${item.open}"> //(선택 변수식으로 item.생략가능)
    <label for="open">판매 오픈</label>

    <button type="submit">전송</button>
</form>

 

실행결과

id값이 "open1"로 설정되어있는 것은 타임리프가 id속성의 유일성을 만족시키기위해 처리한 것이다.

이러면 label태그의 for속성이 깨지게 되는데 그에 대한 해결방법은 each와 checkbox를 함께 적용하면서 알아보자

<form action="">
    <input type="checkbox" id="open1" name="open" value="true">
    <input type="hidden" name="_open" value="on"/>
    <label for="open">판매 오픈</label>

    <button type="submit">전송</button>
</form>

 


each에서 사용하기위한 List컬렉션 코드이다

더보기
@GetMapping("/add-test")
public String addFormTest(Model model) {
    model.addAttribute(new Item());
    Map<String, String> regions = new LinkedHashMap<>();
    regions.put("SEOUL", "서울");
    regions.put("BUSAN", "부산");
    regions.put("JEJU", "제주");
    model.addAttribute("regions", regions);
    return "form/formTest";
}

 

이제 checkbox와 each를 함께 사용해보자

아래 코드는 컨트롤러로부터 받은 List컬렉션 regions에 대한 each처리를 하는 코드이다 실행결과를 먼저 보고 하나하나씩 알아보자

<form th:action >
    <div>등록 지역</div>
    <div th:each="region:${regions}">
        <input type="checkbox" th:field="${item.regions}" th:value="${region.key}">
        <label th:for="${#ids.prev('regions')}" th:text="${region.value}">
        </label>
    </div>

    <button type="submit">전송</button>
</form>

시스템측에서는 regions의 키로 처리하기때문에 value=region.key 사용

->value는 쿼리파라미터형식에서 name=value형태임

 

실행결과

<div>등록 지역</div>
    <div>
        <input type="checkbox" value="SEOUL" id="regions1" name="regions">
        <input type="hidden" name="_regions" value="on"/>
        <label for="regions1">서울</label>
    </div>
    <div>
        <input type="checkbox" value="BUSAN" id="regions2" name="regions">
        <input type="hidden" name="_regions" value="on"/>
        <label for="regions2">부산</label>
    </div>
    <div>
        <input type="checkbox" value="JEJU" id="regions3" name="regions">
        <input type="hidden" name="_regions" value="on"/>
        <label for="regions3">제주</label>
    </div>

 

regions Map

Key Value
SEUOL 서울
BUSAN 부산
JEJU 제주

 

 

먼저 전에 다뤘던 hidden필드를 자동으로 넣어 checkbox가 비활성화되어있을 경우 서버로 false를 전달하도록 해준다.

<input type="hidden" name="_regions" value="on"/>

 

 

label태그의 for속성은 id - for의 값이 같아야 label을 사용하는 의미를 갖을 수 있는데,

타임리프는 id의 필드값+1,2,3을 자동으로 붙혀주기때문에 label의 for속성에서도 ${#dis.prev('필드명')} 문법을 통해 id값을 동적으로 제공하며 id값과 for의 값을 일치시킨다.

<label th:for="${#ids.prev('regions')}" th:text="${region.value}">

 

 

 

value, id, name값은 th:field=${item.regions}에 의해 자동으로 생성된다.

<input type="checkbox" value="SEOUL" id="regions1" name="regions">

 

 

 


위 정리한 내용들은 모두 인프런에서 김영한님의 강의를 듣고 학습한 내용을 스스로 정리한 것임을 밝힙니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1#

 

 

728x90