인프런 커뮤니티 질문&답변

cjkimhello97님의 프로필 이미지
cjkimhello97

작성한 질문수

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술

라디오 버튼

[질문글아님!] 재수정! 공부한 내용 올려봅니다!

작성

·

325

·

수정됨

0

제가 공부하면서 주석으로 정리한 것 있는데

하찮은 실력이지만 다른 공부하시는 분들께 도움이 될 수 있을까 해서 공부한 내용을 올려봅니다!

범위는 "셀렉트박스"까지 내용입니다!

addForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .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 method="post" th:object="${item}">
        <!--
        items.html 에서 상품 등록 버튼을 누름 : th:onclick="|location.href='@{/form/items/add}'|"
        즉, 사용자가 /form/items/add 로 GET 요청이 보내면
        public String addForm(Model model) {...} 호출
        addForm() 로직은
        1. model.addAttirbute("item", new Item()) : 빈 item 객체를 만들고 모델에 담아
        2. addForm.html 에게 전달하는 것이었음

        이제, addForm.html 에서 사용자가 입력값을 넣어 /form/items/add 로 POST 요청을 보낸다면
        @ModelAttribute Item item 이므로 value(입력값)가 object(item객체)의 각 field(item객체의 필드)들에 바인딩되고
        바인딩된 객체는 수정된 객체를 의미하고 이 객체를 item.html로 전달함 (action="item.html")
        addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {...} 호출될 것
        addItem() 로직은
        1. save()
           1-1. sequence(=PK) 증가시키고 sequence를 item 객체의 id로 설정하고(setter)
           1-2. store(=Map)에 id(key)와 item객체(value)를 저장하고
        2. redirectAttributes.addAttribute("itemId", savedItem.getId()) : 해당 객체를 리다이렉트 url 의 경로변수로 사용하고
        2. redirectAttributes.addAttribute("status", true) : 불린값을 리다이렉트 url 의 쿼리 파라미터로 사용하고
        3. "redirect:/form/items/{itemId}" 리다이렉트하는(GET요청) 것이었음

        localhost:8080/form/items/{itemId} 으로 GET 요청이 온다면
        item(@PathVariable long itemId, Model model) {...} 호출할 것
        item() 로직은
        1. findById() : {itemId} 를 사용하여 특정 item 객체를 조회하고
        2. 그 item 객체를 모델에 담아 item.html 에 전달하는 것이었음
        -->
        <div>
            <label for="itemName">상품명</label>
            <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체.

            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
            -->
        </div>
        <div>
            <label for="price">가격</label>
            <input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체.

            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
            -->
        </div>
        <div>
            <label for="quantity">수량</label>
            <input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체.

            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
            -->
        </div>

        <hr class="my-4">

        <!-- single checkbox -->
        <div>판매 여부</div>
        <div>
            <div class="form-check">
                <!--
                spring 은 히든필드를 운용한다.

                HTML checkbox 는 체크되지 않으면 서버로 히든필드만 전송한다. 그럼 spring 은 필드가 false 라고 판단한다.
                HTML 메시지: itemName=22&price=22&quantity=22&_open=on
                spring: item.open = false

                HTML checkbox 가 체크되면 필드와 히든필드를 서버로 전송된다.
                HTML 메시지: itemName=33&price=33&quantity=33&open=on&_open=on. 그럼 spring 은 필드가 true 라고 판단한다.
                spring: item.open = true
                -->

                <input type="checkbox" id="open" th:field="*{open}" class="form-check-input" />
                <!--
                th 를 사용하면 알아서 히든필드를 만들어내기 때문에 HTML checkbox 를 편하게 사용할 수 있다.
                -->

                <label for="open" class="form-check-label">판매 오픈</label>
            </div>
        </div>

        <!-- multi checkbox -->
        <div>
            <div>등록 지역</div>
            <div th:each="region : ${regions}" class="form-check form-check-inline">
                <!--
                ${regions} : 컨트롤러가 호출될 때 @ModelAttribute regions() 가 호출된 결과로 넘어온 완전체 Map<String, String>
                region : Map<String, String> 의 요소. String 객체(key값)
                -->
                <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input">
                <!--
                th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
                *{regions} : ${item.regions}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 List<String> regions 필드는 null
                ${region.key} : 완전체 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값

                사용자가 0개 이상 3개 이하의 체크박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
                -->
                <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label>
                <!--
                ${#ids.prev('필드명')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다.
                ${region.value} : 완전체 Map(통짜)의 value 중 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값
                -->
            </div>
        </div>

        <!-- radio button -->
        <div>
            <div>상품 종류</div>
            <div th:each="type : ${itemTypes}" class="form-check form-check-inline">
                <!--
                ${itemTypes} : 컨트롤러가 호출될 때 @ModelAttribute itemTypes() 가 호출된 결과로 넘어온 완전체 ItemType[] (Enum 배열)
                type : ItemType[] 의 요소. 열거 객체 (반복문이니깐)
                -->
                <input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input">
                <!--
                th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
                *{itemType} : ${item.itemType}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 ItemType itemType 필드는 null
                ${type.name()} : 완전체 ItemType[]의 상수 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐).
                사용자가 3개 중 1개의 라디오박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
                -->
                <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label>
                <!--
                ${#ids.prev('itemType')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. 사용자가 서버로 보낼 값
                ${type.description} : 완전체 ItemType[]의 상수 중 하나의 멤버변수(description). "도서", "음식", "기타" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값
                -->
            </div>
        </div>


        <!-- SELECT -->
        <div>
            <div>배송 방식</div>
            <select th:field="*{deliveryCode}" class="form-select">
                <!--
                *{deliveryCode} : ${item.deliveryCode}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 DeliveryCode deliveryCode 필드는 null

                사용자가 3개 중 1개의 셀렉트를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
                -->
                <option value="">==배송 방식 선택==</option>
                <!--
                맨 위에 보여줄 옵션 설정
                -->
                <option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}"> FAST </option>
                <!--
                그 다음으로 보여줄 옵션들 설정
                ${deliveryCodes} : 컨트롤러가 호출될 때 @ModelAttribute deliveryCodes() 가 호출된 결과로 넘어온 완전체 List<DeliveryCode>
                deliveryCode : List<DeliveryCode> 의 요소. DeliveryCode 객체 (반복문이니깐)
                ${deliveryCode.code} : Delivery 객체의 멤버변수(String code). "FAST", "NORMAL", "SLOW" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값
                ${deliveryCode.displayName} : Delivery 객체의 멤버변수(String displayName). "빠른배송", "일반배송", "느린배송" 중 하나 (반복문이니깐). option 태그 기본값을 대체할 값
                -->
            </select>
        </div>
        <hr class="my-4">


        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button>
            </div>
            <div class="col">
                <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>

editForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
          href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .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 method="post" th:object="${item}">
        <!--
        item.html 에서 상품 수정 버튼을 누름 : th:onclick="|location.href='@{/form/items/{itemId}/edit(itemId=${item.id})}'|"
        즉, 사용자가 /form/items/{itemId}/edit 로 GET 요청이 보내면
        editForm(@PathVariable Long itemId, Model model) {...} 호출
        editForm() 로직은
        1. findByid() : {itemId} 를 사용하여 특정 item 객체를 조회하고
        2. 그 item 객체를 모델에 담아 editForm.html 에 전달하는 것이었음

        이제, editForm.html 에서 사용자가 입력값을 넣어 /form/items/{itemId}/edit 로 POST 요청을 보낸다면
        @ModelAttribute Item item 이므로 value(입력값)가 object(item객체)의 각 field(item객체의 필드)들에 바인딩되고
        바인딩된 객체는 수정된 객체를 의미하고 이 객체를 item.html로 전달함 (action="item.html")
        editItem(@PathVariable Long itemId, @ModelAttribute Item item) {...} 호출될 것
        editItem() 로직은
        1. update()
          1-1. findById() : {itemId} 를 사용하여 수정할 item 객체를 조회하고
          1-2. getter() : 바인딩된 객체(=수정된 객체)의 각 필드들을 조회하고
          1-3. setter() : 수정할 item 객체의 각 필드들을 수정된 객체의 각 필드들로 수정하고
        2. "redirect:/form/items/{itemId}" 리다이렉트하는(GET요청) 것이었음

        localhost:8080/form/items/{itemId} 으로 GET 요청이 온다면
        item(@PathVariable long itemId, Model model) {...} 호출할 것
        item() 로직은
        1. findById() : {itemId} 를 사용하여 특정 item 객체를 조회하고 그 item객체를 모델에 담아
        2. item.html 에 전달하는 것이었음
        -->
        <div>
            <label for="id">상품 ID</label>
            <input type="text" id="id" class="form-control" th:field="*{id}" readonly>
            <!--
            th 가 name, value, id 를 만들어준다.
            *{id}: ${item.id}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님)
            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨
            -->
        </div>
        <div>
            <label for="itemName">상품명</label>
            <input type="text" id="itemName" class="form-control" th:field="*{itemName}">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{itemName}: ${item.itemName}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님)
            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴
            -->
        </div>
        <div>
            <label for="price">가격</label>
            <input type="text" id="price" class="form-control" th:field="*{price}">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{itemName}: ${item.itemName}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님)
            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴
            -->
        </div>
        <div>
            <label for="quantity">수량</label>
            <input type="text" id="quantity" class="form-control" th:field="*{quantity}">
            <!--
            th 가 name, value, id 를 만들어준다.
            *{quantity}: ${item.quantity}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님)
            사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴
            -->
        </div>

        <hr class="my-4">
        <!-- single checkbox -->
        <div>판매 여부</div>
        <div>
            <div class="form-check">
                <!--
                spring 은 히든필드를 운용한다.

                HTML checkbox 는 체크되지 않으면 서버로 히든필드만 전송한다. 그럼 spring 은 필드가 false 라고 판단한다.
                HTML 메시지: itemName=22&price=22&quantity=22&_open=on
                spring: item.open = false

                HTML checkbox 가 체크되면 필드와 히든필드를 서버로 전송된다.
                HTML 메시지: itemName=33&price=33&quantity=33&open=on&_open=on. 그럼 spring 은 필드가 true 라고 판단한다.
                spring: item.open = true
                -->

                <input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
                <!--
                th 가 name, value, id 를 만들어준다.
                *{open} : ${item.open}의 생략 버전. editForm() 에서 model 에 담겨져 넘어온 특정 item 객체의 open(Boolean객체)
                사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴
                -->
                <label for="open" class="form-check-label">판매 오픈</label>
            </div>
        </div>

        <!-- multi checkbox -->
        <div>
            <div>등록 지역</div>
            <div th:each="region : ${regions}" class="form-check form-check-inline">
                <!--
                ${regions} : 컨트롤러가 호출될 때 @ModelAttribute regions() 가 호출된 결과로 넘어온 완전체 Map<String, String>
                region : Map<String, String> 의 요소. String 객체(key값)
                -->
                <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input">
                <!--
                th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
                *{regions} : ${item.regions}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 List<String> regions 필드는 null
                ${region.key} : 완전체 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값

                사용자가 0개 이상 3개 이하의 체크박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴
                -->
                <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label>
                <!--
                ${#ids.prev('필드명')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다.
                ${region.value} : 완전체 Map(통짜)의 value 중 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값
                -->
            </div>
        </div>


        <!-- radio button -->
        <div>
            <div>상품 종류</div>
            <div th:each="type : ${itemTypes}" class="form-check form-check-inline">
                <!--
                ${itemTypes} : 컨트롤러가 호출될 때 @ModelAttribute itemTypes() 가 호출된 결과로 넘어온 완전체 ItemType[] (Enum 배열)
                type : ItemType[] 의 요소. 열거 객체 (반복문이니깐)
                -->
                <input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input">
                <!--
                th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
                *{itemType} : ${item.itemType}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 ItemType itemType 필드는 null
                ${type.name()} : 완전체 ItemType[]의 상수 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐).

                사용자가 3개 중 1개의 라디오박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴
                -->
                <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label>
                <!--
                ${#ids.prev('itemType')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. 사용자가 서버로 보낼 값
                ${type.description} : 완전체 ItemType[]의 상수 중 하나의 멤버변수(description). "도서", "음식", "기타" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값
                -->
            </div>
        </div>


        <!-- SELECT -->
        <div>
            <div>배송 방식</div>
            <select th:field="*{deliveryCode}" class="form-select">
                <!--
                *{deliveryCode} : ${item.deliveryCode}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 DeliveryCode deliveryCode 필드는 null

                사용자가 3개 중 1개의 셀렉트를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴
                -->
                <option value="">==배송 방식 선택==</option>
                <!--
                맨 위에 보여줄 옵션 설정
                -->
                <option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}"> FAST </option>
                <!--
                그 다음으로 보여줄 옵션들 설정
                ${deliveryCodes} : 컨트롤러가 호출될 때 @ModelAttribute deliveryCodes() 가 호출된 결과로 넘어온 완전체 List<DeliveryCode>
                deliveryCode : List<DeliveryCode> 의 요소. DeliveryCode 객체 (반복문이니깐)
                ${deliveryCode.code} : Delivery 객체의 멤버변수(String code). "FAST", "NORMAL", "SLOW" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값
                ${deliveryCode.displayName} : Delivery 객체의 멤버변수(String displayName). "빠른배송", "일반배송", "느린배송" 중 하나 (반복문이니깐). option 태그 기본값을 대체할 값
                -->
            </select>
        </div>


        <div class="row">
            <div class="col">
                <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button>
            </div>
            <div class="col">
                <button class="w-100 btn btn-secondary btn-lg"
                        onclick="location.href='item.html'"
                        th:onclick="|location.href='@{/form/items/{itemId}(itemId=${item.id})}'|"
                        type="button">취소</button>
            </div>
        </div>

    </form>

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

item.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <link th:href="@{/css/bootstrap.min.css}"
            href="../css/bootstrap.min.css" rel="stylesheet">
    <style>
        .container {
            max-width: 560px;
        }
    </style>
</head>
<body>

<!--
${param.status} : addItem() 또는 editItem() 에서 "redirect:/form/items/{itemId}" 로 리다이렉트해왔음
"redirect:/form/items/{itemId}" url 로 요청이 들어왔기 때문에 item() 로직 수행됨
item() 로직에서 경로변수 인자를 사용하여 특정 item 객체를 조회하고
item 객체를 model에 담고
item.html 로 보냄
-->

<div class="container">

    <div class="py-5 text-center">
        <h2>상품 상세</h2>
    </div>

    <!-- 추가 -->
    <h2 th:if="${param.status}" th:text="'저장 완료'"></h2>
    <!--
    ${param.status} : addItem()에서 "redirect:/form/items/{itemId}" 로 리다이렉트해왔음
    즉, http://localhost:8080/form/items/3?status=true 이런 식으로 리다이렉트

    (addItem()에서 redirectAttributes 로 itemId, status 를 설정했음. itemId, status는 한번 설정하면 불변해야 하므로 editItem()에선 rdirectAttributes 설정하지 않음)
    itemId 는 이미 리다이렉트 url로 사용되었고
    status 는
    param 으로 url 경로변수에 접근하여 속성을 추출하여 사용하면 된다.
    -->


    <div>
        <label for="itemId">상품 ID</label>
        <input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly>
        <!--
        th 가 name, value, id 를 만들어준다.
        ${item.id} : 사용자가 addForm.html 또는 editForm.html 에서 입력값을 넣어 POST 요청을 보내면 value가 서버로 넘어갔었다.
        서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드에 매핑(setter)했었다.
        그 Item 객체가 FormItemController 의 addItem() 또는 editItem()에 의해 item.html 로 리다이렉트되었다.
        그 Item 객체를 item 라는 이름으로  다시 각각 add() 또는 editItem()이 호출되어 리다이렉트할 때 넘겨준 Item 객체
        사용자가 입력값을 넣어 POST 요청을 보내면 value가 서버로 넘어가고, 서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드(Long id)에 매핑한다(setter)
        이후 form 태그의 action 과 method 에 따라 행동하면 된다
        -->
    </div>
    <div>
        <label for="itemName">상품명</label>
        <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly>
        <!--
        *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체.
        사용자가 입력값을 넣어 POST 요청을 보내면 서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드(String itemName)에 매핑한다(setter)
        이후 form 태그의 action 과 method 에 따라 행동하면 된다
        <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" class="form-control" value="10000" th:value="${item.price}" readonly>
    </div>
    <div>
        <label for="quantity">수량</label>
        <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly>
    </div>

    <hr class="my-4">
    <!-- single checkbox -->
    <div>판매 여부</div>
    <div>
        <div class="form-check">
            <input type="checkbox" id="open" th:field="${item.open}" class="form-check-input" disabled>
            <!--
            th 가 name, value, id 을 만들어줌
            체크박스가 체크되어 있다면 checked="checked" 속성까지 만들어줌. 체크되지 않았다면 속성 만들어지지 않음.
            -->
            <label for="open" class="form-check-label">판매 오픈</label>
        </div>
    </div>

    <!-- multi checkbox -->
    <div>
        <div>등록 지역</div>
        <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담았던 것 반복문 돌리기 -->
            <!--
            @ModelAttribute 에 담겨서 넘어온 것(regions()) 반복문 돌리기
            ${regions} : Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문.
            -->
            <input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled>
            <!--
            th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
            ${item.regions} : List. 사용자가 선택했던 요소들이 담긴 리스트이므로 ["SEOUL", "BUSAN"] 등의 리스트.
            ${region.key} : 위 div 태그의 Map의 key 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐)
            value("SEOUL", "BUSAN", "JEJU" 중 하나)가 field(Map<String, String>) 의 요소에 존재한다면 th가 checked="checked" 속성을 만들어줌.
            -->
            <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label>
            <!--
            ${#ids.prev('필드명')} : 바로 이전 input 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다.
            ${region.value} : 위 div 태그의 Map의 value 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐)
            -->
        </div>
    </div>


    <!-- radio button -->
    <div>
        <div>상품 종류</div>
        <div th:each="type : ${itemTypes}" class="form-check form-check-inline">
            <!--
            @ModelAttribute 에 담겨서 넘어온 것(itemTypes()) 반복문 돌리기
            ${itemTypes} : ItemType[](ENUM배열). 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문.
            -->
            <input type="radio" th:field="${item.itemType}" th:value="${type.name()}" class="form-check-input" disabled>
            <!--
            th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다.
            ${item.itemType} : ENUM. 사용자가 선택했던 ENUM.
            ${type.name()} : 위 div 태그의 ENUM배열의 ENUM 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐). 사용자가 각 라디오박스를 체크해서 POST 요청 보낼 때 item.itemType 에 문자열이 바인딩되어 날아감. 이후 서버에서 다시 상수로 변환하여 itemType에 저장함.
            value("BOOK", "FOOD", "ETC" 중 하나)가 field(사용자가 선택했던 ENUM)와 일치한다면 th가 checked="checked" 속성을 만들어줌.
            -->
            <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label>
            <!--
            ${#ids.prev('필드명')} : 바로 이전 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다.
            ${type.description} : 위 div 태그의 ENUM배열의 ENUM 중 하나의 값. "도서", "음식", "기타" 중 하나 (반복문이니깐).
            -->
        </div>
    </div>


    <div class="row">
        <div class="col">
            <button class="w-100 btn btn-primary btn-lg"
                    onclick="location.href='editForm.html'"
                    th:onclick="|location.href='@{/form/items/{itemId}/edit(itemId=${item.id})}'|"
                    type="button">상품 수정</button>
        </div>
        <div class="col">
            <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>

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

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.

공부한 내용을 공유해주셔서 감사합니다! 다른 분들께 도움이 될 수 있을 것 같아요.

주신 코드는 HTML과 Thymeleaf를 사용한 상품 등록 폼인 것 같습니다. form 태그를 통해 사용자가 상품 정보를 입력한 후 POST 요청을 보내면, “/form/items/add” URL로 요청이 전송되고 이에 대한 처리를 위해 addForm() 메서드가 호출됩니다.

addForm() 메서드는 빈 Item 객체를 생성하고 모델에 담아 addForm.html로 전달하는 역할을 하고 있습니다. 이를 통해 사용자는 입력한 상품 정보를 addForm.html로 전송할 수 있게 됩니다.

더 자세한 내용은 해당 코드의 뒷부분에 있는 로직을 참고하시면 도움이 될 거예요. 필요한 부분이 있다면 추가로 질문해주세요. 감사합니다.

cjkimhello97님의 프로필 이미지
cjkimhello97

작성한 질문수

질문하기