오늘은 HTML 체크박스의 동작 방식과 타임리프(Thymeleaf)에서 제공하는 편리한 기능에 대해 알아보려고 한다.
체크박스는 "판매 여부", "약관 동의", "활성화 여부"처럼 Boolean 값을 표현할 때 자주 사용된다. 하지만 체크박스에는 중요한 특징이 있다. 체크하지 않으면 값 자체가 서버로 넘어가지 않는다는 점이다.
단순 등록 화면에서는 크게 문제되지 않지만, 수정 화면에서는 심각한 문제로 이어질 수 있다. 예를 들어 원래 체크되어 있던 값을 사용자가 해제했는데, 서버로 아무 값도 넘어오지 않으니 데이터가 갱신되지 않는 상황이 생기는 것이다.
이 글에서는
- 단순 HTML 체크박스가 동작하는 방식
- 체크 해제를 인식하기 위한 히든 필드(hidden field) 기법
- 타임리프가 제공하는 자동 처리 기능
을 차례대로 살펴본다.
1. 단순 HTML 체크박스 동작
예를 들어, 상품 등록 화면에 체크박스를 하나 두었다고 해보자.
<!-- addForm.html -->
<div>판매 여부</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>
컨트롤러에서는 단순히 넘어온 값을 로그로 찍어본다.
@PostMapping("/add")
public String addItem(Item item) {
log.info("item.open={}", item.getOpen());
return "redirect:/items";
}
실행 결과는 다음과 같다.
- 체크박스를 선택한 경우
item.open=true
- 체크박스를 선택하지 않은 경우
item.open=null
즉, 체크하지 않았을 때는 값이 아예 서버로 넘어오지 않기 때문에 null이 된다.
2. 왜 문제가 될까?
등록 화면에서는 큰 문제가 없어 보일 수 있다. 하지만 수정 화면에서는 이야기가 달라진다.
예를 들어, 원래 "판매 오픈"이 체크된 상태였는데 사용자가 체크를 해제하고 저장을 눌렀다고 해보자.
- 체크된 경우에는 open=true 값이 넘어오니 정상적으로 반영된다.
- 하지만 해제한 경우에는 값 자체가 전송되지 않는다.
이 말은 서버 입장에서 "값을 변경하지 않은 것"처럼 보이게 되고, 결국 DB의 값이 그대로 남아 버리는 문제가 생긴다.
즉, 사용자는 분명 체크를 해제했는데, 저장 후에도 여전히 체크된 상태로 보이는 버그가 발생할 수 있다.
3. 전통적인 해결책 – 히든 필드
이 문제를 해결하기 위해 스프링 MVC는 작은 트릭을 제공한다. 체크박스와 함께 같은 이름 앞에 언더스코어(_)가 붙은 **히든 필드(hidden field)**를 추가하는 것이다.
<div>판매 여부</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>
- 체크박스를 선택한 경우
open=on&_open=on → item.open=true
- 체크박스를 선택하지 않은 경우
_open=on → item.open=false
즉, 히든 필드를 사용하면 체크하지 않은 경우에도 false로 명확하게 인식된다.
open이 서버에 넘어오지 않는다면 내부에서 히든필드를 통해 인식해 open을 false로 바꿔주기 떄문이다.
4. 하지만 히든 필드도 번거롭다
문제는 개발자가 매번 이런 히든 필드를 직접 추가해야 한다는 것이다.
간단한 화면이라면 괜찮지만, 체크박스가 여러 개라면 코드가 복잡해지고, 실수로 빼먹으면 버그로 이어질 수 있다.
5. 타임리프가 제공하는 해결책
타임리프는 이 과정을 자동으로 처리해준다. 개발자는 단순히 th:field만 사용하면 된다.
<div>판매 여부</div>
<div class="form-check">
<input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
렌더링 결과를 확인해보면 타임리프가 자동으로 히든 필드를 추가해준다.
<div>판매 여부</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>
실행 로그도 정상적으로 잘 찍힌다.
- 체크 O → item.open=true
- 체크 X → item.open=false
마무리하며
체크박스는 간단해 보이지만, 체크하지 않았을 때 값이 아예 넘어가지 않는다는 점 때문에 수정 화면에서 쉽게 버그를 만들 수 있다. 전통적으로는 히든 필드를 함께 추가해 문제를 해결했지만, 매번 이를 처리하는 것은 번거롭다.
타임리프는 이런 불편을 해결하기 위해 th:field를 제공한다. 이를 사용하면 히든 필드까지 자동으로 생성되므로, 체크 해제 여부까지 정확하게 서버에 반영된다.
즉, 타임리프 덕분에 체크박스의 번거로운 처리 과정을 걱정하지 않고 개발할 수 있다.
감사합니다.
'타임리프' 카테고리의 다른 글
| [Thymeleaf] 템플릿 레이아웃 (0) | 2025.09.16 |
|---|---|
| [Thymeleaf] 템플릿 조각 (0) | 2025.09.16 |
| [Thymeleaf] 자바스크립트 인라인 (0) | 2025.09.16 |
| [Thymeleaf] 블록 (0) | 2025.09.16 |
| [Thymeleaf] 주석 (0) | 2025.09.16 |
