본문 바로가기
Thymeleaf

[Thymeleaf] 체크박스

by 걸어가는 신사 2022. 1. 28.

1. 단일 체크 박스

(1) 순수 HTML 체크 박스

<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>
//Controller 로깅
FormItemController : item.open=true //체크 박스를 선택하는 경우

FormItemController : item.open=null //체크 박스를 선택하지 않는 경우
  • 체크 박스를 체크하면 HTML Form에서 open=on 이라는 값이 넘어간다. (브라우저에서 확인 가능)
  • 스프링은 on이라는 문제를 true 타입으로 변환해준다.
    • 스프링 타입 컨버터가 이 기능을 수행

* 주의

  • HTML에서 체크 박스를 선택하지 않고 폼을 전송하면 open이라는 필드 자체가 서버로 전송되지 않는다.
  • Controller에서 로그를 찍어보면 체크를 하지 않았을 때 null로 값이 넘어간다.
수정의 경우에는 상황에 따라서 이 방식이 문제가 될 수 있다. 사용자가 의도적으로 체크되어 있던 값을 체크를 해제해도 저장 시 아무 값도 넘어가지 않기 때문에, 서버 구현에 따라서 값이 오지 않은 것으로 판단해서 값을 변경하지 않을 수도 있다.

(2) 순수 HTML 체크박스 문제 해결

  • 히든 필드를 이용해서 "_open" 처럼 기존 체크 박스 이름 앞에 언더스코어(_)를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다.
    • 히든 필드는 항상 전송된다.
<div>
    <div class="form-check">
        <input type="checkbox" id="open" name="open" class="form-check-input">
        <input type="hidden" name="_open" value="on" > 
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>
  • 체크를 해제한 경우 open은 전송되지 않고, _open만 전송되는데, 스프링 MVC는 체크를 해제했다고 판단한다.
FormItemController : item.open=true //체크 박스를 선택하는 경우
FormItemController : item.open=false //체크 박스를 선택하지 않는 경우

(3) 타임리프 사용해서 해결

  • 타임리프가 제공하는 폼 기능을 사용하면 이런 부분을 자동으로 처리할 수 있다.
<div>
    <div class="form-check">
        <input type="checkbox" id="open"  th:field="${item.open}" class="form-check-input">
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>
  • th:field를 사용하면 히든 필드 부분이 자동으로 생성되어 있다.
<!-- 타임리프 체크 박스 렌더링 이후 -->
<div>
    <div class="form-check">
        <input type="checkbox" id="open" class="form-check-input" name="open"
               value="true">
        <input type="hidden" name="_open" value="on"/>
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>

 

*저장되어있는 값을 불러와서 체크 박스에 출력

<div>
    <div class="form-check">
        <input type="checkbox" id="open"  th:field="${item.open}" class="form-check-input" disabled>
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>
<!-- 렌더링 이후 -->
<div class="form-check">
    <input type="checkbox" id="open" class="form-check-input" disabled
           name="open" value="true" checked="checked">
    <label for="open" class="form-check-label">판매 오픈</label>
</div>
  • disabled를 사용해서 상품 상세에서는 체크 박스가 선택되지 않도록 하였다.
  • chcked="chekced" (disabled 적용 & 값이 true이면 자동으로 생성)
    • 체크 박스에서 판매 여부를 선택해서 저장하면, 조회 시에 checked 속성이 추가된 것을 확인할 수 있다.
    • th:field를 사용하면, 값이 true인 경우 체크를 자동으로 처리해준다.

 

2. 멀티 체크 박스

  • 체크 박스를 멀티로 사용해서, 하나 이상을 체크하는 경우
//Controller
@ModelAttribute("regions")
public Map<String, String> regions() {
    Map<String, String > regions = new LinkedHashMap<>();
    regions.put("SEOUL", "서울");
    regions.put("BUSAN", "부산");
    regions.put("JEJU", "제주");
    return regions;
}
  • 서울, 부산, 제주라는 체크 박스를 반복해서 보여주려고 한다.
  • model에 Map을 담아서 View로 넘겨준다.
<div th:each="region : ${regions}" class="form-check form-check-inline">
    <input type="checkbox" th:field="${item.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>

출력 화면

  • 멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다.
  • 이렇게 반복해서 HTML 태그를 생성할 때, 생성된 HTML 태그 속성에서 name은 같아도 되지만, id는 모두 달라야 한다.
  • 타임리프는 체크박스를 each 루프 안에 반복해서 만들 때 임의로 1, 2, 3 숫자를 자동으로 뒤에 붙여준다.
th:for="${#ids.prev('regions')}"
  • HTML의 id가 타임리프에 의해 동적으로 만들어지기 때문에 <label for="id 값">으로 label의 대상이 되는 id 값을 임의로 지정하는 것이 곤란하다.
  • 타임리프는 ids.prev(...), ids.next(...)을 제공해서 동적으로 생성되는 id 값을 사용할 수 있다.
<!-- 타임리프 HTML 생성 결과 -->
<div class="form-check form-check-inline">
    <input type="checkbox" value="SEOUL" class="form-check-input"
           id="regions1" name="regions">
    <input type="hidden" name="_regions" value="on"/>
    <label for="regions1"
           class="form-check-label">서울</label>
</div>
<div class="form-check form-check-inline">
    <input type="checkbox" value="BUSAN" class="form-check-input"
           id="regions2" name="regions">
    <input type="hidden" name="_regions" value="on"/>
    <label for="regions2"
           class="form-check-label">부산</label>
</div>
<div class="form-check form-check-inline">
    <input type="checkbox" value="JEJU" class="form-check-input"
           id="regions3" name="regions">
    <input type="hidden" name="_regions" value="on"/>
    <label for="regions3"
           class="form-check-label">제주</label>
</div>
  • <label for="id 값">에 지정된 id가 checkbox에서 동적으로 생성된 regions1, regions2, regions3에 맞추어 순서대로 입력된 것을 확인할 수 있다.
  • hidden input으로 _regions를 넘겨서 웹브라우저에서 체크를 하나도 하지 않았을 때, 클라이언트가 서버에 아무런 데이터를 보내지 않는 것을 방지한다. (_regions 조차 보내지 않으면 null)
regions=SEOUL&_regions=on®ions=BUSAN&_regions=on&_regions=on
=> item.regions=[SEOUL, BUSAN]
_regions=on&_regions=on&_regions=on
=> item.regions=[]
반응형

댓글