코드 그라데이션

Thymeleaf 통합 (2) 체크 박스 - 단일(1) 본문

Spring/Thymeleaf

Thymeleaf 통합 (2) 체크 박스 - 단일(1)

완벽한 장면 2023. 12. 2. 10:40

체크 박스 - 단일1

단순 HTML 체크 박스

resources/templates/form/addForm.html 추가

<!-- single checkbox -->
<div>판매 여부</div>

<div>
<!-- 또 다른 <div> 요소로, 이 부분은 아래에 있는 체크박스를 묶는 역할. -->

  <div class="form-check">
  <!-- <div class="form-check">은 폼(form) 요소를 스타일링하고 구성하기 위한 CSS 클래스를 가진 <div> 요소. -->

    <input type="checkbox" id="open" name="open" class="form-check-input">
    <!-- <input> 요소는 사용자로부터 정보를 입력받는데 사용됩니다. 
    이 경우, type="checkbox" 속성을 가지고 있어 "체크박스". 
    id 속성은 고유한 식별자로 사용되며, 
    name 속성은 서버로 데이터를 전송할 때 사용되는 필드의 이름. 
    class 속성은 스타일링을 위한 CSS 클래스를 지정. -->

    <label for="open" class="form-check-label">판매 오픈</label>
    <!-- <label> 요소는 사용자 인터페이스의 라벨. 
    for 속성은 라벨과 연결된 입력 요소를 지정. 
    이 경우, "판매 오픈" 라벨은 id가 "open"인 체크박스와 연결되어 
    체크박스를 클릭할 때 라벨을 클릭한 것과 같은 효과. 
    class 속성은 라벨을 스타일링하기 위한 CSS 클래스를 지정. -->

  </div>
</div>

 

resources/templates/form/addForm.html  전체 코드

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <!-- 부트스트랩 CSS 파일을 불러옵니다. -->
    <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        /* 스타일 정의: .container 클래스에 최대 너비를 설정합니다. */
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<div class="container">

    <div class="py-5 text-center">
        <!-- 페이지 제목을 출력합니다. -->
        <h2>상품 등록 폼</h2>
    </div>

    <form action="item.html" th:action th:object="${item}" method="post">
        <div>
            <!-- 상품명 입력 필드와 레이블 -->
            <label for="itemName">상품명</label>
            <input type="text" id="itemName" name="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
        </div>
        <div>
            <!-- 가격 입력 필드와 레이블 -->
            <label for="price">가격</label>
            <input type="text" id="price" name="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
        </div>
        <div>
            <!-- 수량 입력 필드와 레이블 -->
            <label for="quantity">수량</label>
            <input type="text" id="quantity" name="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
        </div>

        <hr class="my-4">

        <!-- 추가! -->
        <!-- single checkbox -->
        <div>판매 여부</div>
        <div>
            <div class="form-check">
                <input type="checkbox" id="open" name="open" class="form-check-input"> <!-- item의 open 필드 -->
                <label for="open" class="form-check-label">판매 오픈</label>
            </div>
        </div>


        <div class="row">
            <div class="col">
                <!-- 상품 등록 버튼 -->
                <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button>
            </div>
            <div class="col">
                <!-- 취소 버튼: 버튼 클릭 시 'items.html' 페이지로 이동합니다. -->
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='items.html'"
                        th:onclick="|location.href='@{/form/items}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>

</div> <!-- /container -->
</body>
</html>

 

상품이 등록되는 곳에 다음과 같이 로그를 남겨서 값이 잘 넘어오는지 확인

FormItemController 추가

@Slf4j 클래스 레벨에 추가

@GetMapping("/add")
public String addForm(@ModelAttribute Item item, Model model) { // 추가
    // 로깅 추가
    log.info("item.open={}", item.getOpen());
    model.addAttribute("item", new Item());
    return "form/addForm";
}

 

실행하면

1
2

 

F12를 확인해보면

3

 

체크박스에 체크했더니 로그 찍어보니까 true로 나옴.

4

 

반대로

5

 

이렇게 넘기면

6

일단 저장은 되었고

7

뒤이어서 null 이 찍힌다.

 


open : on이 없음.

값 자체가 넘어가지 않음을 확인할 수 있음.

 

 


HTTP 요청 메시지 로깅

HTTP 요청 메시지를 서버에서 보고 싶으면 다음 설정을 추가하면 된다.

application.properties

logging.level.org.apache.coyote.http11=debug

 

이거 틀고 체크 하고 등록을 다시 진행해보면

 

반대로 체크 없이 등록을 진행하면

 

HTML checkbox는 선택이 안되면 클라이언트에서 서버로 값 자체를 보내지 않는다. 

수정의 경우에는 상황에 따라서 이 방식이 문제가 될 수 있다. 

 

사용자가 의도적으로 체크되어 있던 값을 체크를 해제해도 저장 시 아무 값도 넘어가지 않기 때문에,

서버 구현에 따라서 값이 오지 않은 것으로 판단해서 값을 변경하지 않을 수도 있다.

 


 

이런 문제를 해결하기 위해서 스프링 MVC는 약간의 트릭을 사용하는데, 히든 필드를 하나 만들어서,
_open 처럼 기존 체크 박스 이름 앞에 언더스코어( _ )를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다. 

 

히든 필드는 항상 전송된다. 따라서 체크를 해제한 경우 여기에서 open 은 전송되지 않고, _open 만 전송되는데, 

이 경우 스프링 MVC는 체크를 해제했다고 판단한다.

 

addForm.html 에 추가

<!-- single checkbox -->
<div>판매 여부</div>
<div>
    <div class="form-check">
        <input type="checkbox" id="open" name="open" class="form-check-input"> <!-- item의 open 필드 -->
        <!-- 히든 필드 추가-->
        <input type="hidden" name="_open" value="on"/>
        <label for="open" class="form-check-label">판매 오픈</label>
    </div>
</div>

 

실행 다시 하면

1
2

 

로그는

3

문제없고,

 

 

한편 체크 안 하고 등록해보면

4
5

로그는

6

히든 필드는 무조건 전송됨을 확인할 수가 있음.

 

여기서 주목할 부분은 이거다.

null 이 아니라 false 가 들어가 있다는 점.

 

체크박스는 미 체크 시 아예 값을 안 넘기는 태생적 한계가 있다.

그래서 트릭처럼 하나 만들어서 사용하는 것. 

 


실행 로그

 

체크 박스 체크

 

체크 박스 미체크

 

개발자는 true, false 를 보고 판단한다면 엄청나게 편리

 

그런데 할 때마다 내가 직접 히든 필드를 넣어줘야 한다.

이 마저도 타임리프가 해결하는 기능이 있다...!

728x90
Comments