오봉이와 함께하는 개발 블로그

스프링 MVC 2 - 체크 박스 단일 본문

BE/Thymeleaf

스프링 MVC 2 - 체크 박스 단일

오봉봉이 2022. 8. 17. 21:40
728x90

체크 박스 - 단일1

단순 HTML 체크 박스

resources/templates/form/addForm.html에 추가하자

<hr class="my-4">
<!-- single checkbox -->
<div>판매 여부</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>
</div>

FormItemController 추가

@PostMapping("/add")
public String addItem(Item item, RedirectAttributes redirectAttributes) {
    log.info("item.open={}", item.getOpen());
    ...
}

FormItemController@Slf4j추가

실행 로그

FormItemController : item.open=true //체크 박스를 선택하는 경우
FormItemController : item.open=null //체크 박스를 선택하지 않는 경우

체크 박스를 체크하면 HTML Form에서 원래는 open=on이라는 값이 넘어가는데, 스프링은 on이라는 문자를 true타입으로 반환해준다.
(스프링 타입 컨버터가 이 기능을 수행한다)
HTML에서 체크 박스를 선택하지 않고 폼을 전송하면 open이라는 필드 자체가 서버로 전송되지 않는다.

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

이런 문제 해결을 위해 스프링 MVC에서 트릭을 사용한다.
히든 필드를 하나 만들어서 기존 체크 박스 이름 앞에 언더바(_)를 붙여서 정송하면 체크를 해제했다고 인식할 수 있다.
히든 필드는 항상 전송되기 때문에 체크를 해제한 경우 여기서 open은 전송되지 않고, _open만 전송되면 이 경우 스프링 MVC는 체크를 해제했다고 판단한다.

예시

<!-- single checkbox -->
<div>판매 여부</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>
</div>
FormItemController : item.open=true //체크 박스를 선택하는 경우
FormItemController : item.open=false //체크 박스를 선택하지 않는 경우

체크 박스 체크
open=on&_open=on
체크 박스를 체크하면 스프링 MVC가 open에 값이 있는 것을 확인하고 사용한다.
이때 _open은 무시한다.

체크 박스 미체크
_open=on
체크 박스를 체크하지 않으면 스프링 MVC가 _open만 있는 것을 확인하고, open의 값이 체크되지 않았다고 인식한다.
이 경우 서버에서 로그를 찍어보면 null이 아니라 false인 것을 확인할 수 있다.



체크 박스 - 단일2

개발할 때 마다 이렇게 히든 필드를 추가하는 것은 상당히 번거롭다.
타임리프가 제공하는 폼 기능을 사용하면 이런 부분을 자동으로 처리할 수 있다.

타임리프 - 체크 박스 코드 추가

<form action="item.html" th:object="${item}" th:action method="post">
<!-- 상위 코드 -->
<!-- single checkbox -->
<div>판매 여부</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>

th:object를 사용하지 않았다면 th:field="${item.open}"을 사용해야 한다.

타임리프 체크 박스 HTML 생성 결과

<!-- single checkbox -->
<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>
</div>

타임리프를 사용하면 체크 박스의 히든 필드와 관련된 부분을 입력하지 않아도 자동으로 입력되어 있게 해준다.
HTML 생성 결과를 보면 히든 필드 부분이 자동으로 생성되어 있다.

실행 로그

FormItemController : item.open=true //체크 박스를 선택하는 경우
FormItemController : item.open=null //체크 박스를 선택하지 않는 경우

상품 상세 적용

<!-- single checkbox -->
<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>

주의 : item.html에는 th:object를 사용하지 않았기 때문에 th:field부분에 ${item.open}으로 적어주어야 한다.
disabled를 사용해서 상품 상세에서는 체크 박스가 선택되지 않도록 했다.

HTML 생성 결과

<hr class="my-4">
<!-- single checkbox -->
<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>

타임리프의 체크 확인
checked="checked"가 되어 있다.
체크 박스에서 선택해서 저장하면 조회할 때 checked속성이 추가된 것을 확인할 수 있다.
이런 부분을 개발자가 직접 처리하려면 조건을 따로 달아서 보여줘야 하는 번거로움이 있는데, th:field를 사용했을 때 값이 true면 체크를 자동으로 처리해준다.

상품 수정 적용

상품 수정도 th:object, th:field를 모두 적용해야 한다.

<form action="item.html" th:object="${item}" th:action method="post">
<!-- 기존 코드 -->
<!-- single checkbox -->
<div>판매 여부</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>

이 상태로만 실행하면 체크 박스를 수정해도 반영되지 않기 때문에 실제 반영되도록 코드를 추가하자

@Repository
public class ItemRepository {

    private static final Map<Long, Item> store = new HashMap<>(); //static
    private static long sequence = 0L; //static

    // 기존 코드

    public void update(Long itemId, Item updateParam) {
        Item findItem = findById(itemId);
        findItem.setItemName(updateParam.getItemName());
        findItem.setPrice(updateParam.getPrice());
        findItem.setQuantity(updateParam.getQuantity());
        findItem.setOpen(updateParam.getOpen());
        findItem.setRegions(updateParam.getRegions());
        findItem.setItemType(updateParam.getItemType());
        findItem.setDeliveryCode(updateParam.getDeliveryCode());
    }

    // 기존 코드
}
출처 : 인프런 김영한 지식공유자님 강의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
728x90
Comments