일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- HWPF
- SQLSTATE=42705
- getChannel()
- 앵커멤버
- dm_exec_requests
- 아이
- 자바
- 디스패처서블릿
- SQL
- 프론트컨트롤러
- 함수
- spring
- 교육법
- renameTo
- git
- ERROR_MESSAGE
- XWPF
- 진경혜
- 스프링
- 튜닝
- 요청매핑
- transferTo
- 재귀멤버
- java
- XACT_STATE
- TRANCOUNT
- 배치
- MSSQL
- 홈스쿨링
- 요약
- Today
- Total
필기노트
타임리프 스프링 통합과 폼 본문
목차
1. 설정방법
2. 입력 폼 처리
3. 체크박스 - 단일
4. 체크박스 - 멀티
5. 라디오버튼
6. 셀렉트박스
1. 설정방법
스프링 부트는 build.gradle 에 다음 한줄을 넣어주면 Gradle은 타임리프와 관련된 라이브러리를 다운로드 받고, 스프링 부트는 타임리프와 관련된 설정용 스프링 빈을 자동으로 등록해준다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
2. 입력 폼 처리
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "form/editForm";
}
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"> </div>
<div>
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
</div>
- th:object="${item}" : <form> 에서 사용할 객체를 지정한다. 선택 변수 식( *{...} )을 적용할 수 있다.
- th:field="*{itemName}" : ${item.itemName} 과 같다. 앞서 th:object 로 item을 선택했기 때문에 선택 변수 식을 적용할 수 있다. th:field 는 id , name , value 속성을 모두 자동으로 만들어준다.
- id : th:field 에서 지정한 변수 이름과 같다. id="itemName"
- name : th:field 에서 지정한 변수 이름과 같다. name="itemName"
- value : th:field 에서 지정한 변수의 값을 사용한다. value=""
렌더링 전
<input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
렌더링 후
<input type="text" id="itemName" class="form-control" name="itemName" value="itemA">
많은 부분이 th:field 덕분에 자동으로 처리되는 것을 확인할 수 있다.
3. 체크박스 - 단일
<div class="form-check">
<input type="checkbox" id="open" name="open" class="form-check-input">
<label for="open" class="form-check-label">판매 오픈</label>
</div>
HTML checkbox는 선택이 안되면 클라이언트에서 서버로 값 자체를 보내지 않는다.
수정의 경우에는 상황에 따라서 이 방식이 문제가 될 수 있다. 사용자가 의도적으로 체크되어 있던 값을 체크를 해제해도 저장시 아무 값도 넘어가지 않기 때문에, 서버 구현에 따라서 값이 오지 않은 것으로 판단해서 값을 변경하지 않을 수도 있다.
이런 문제를 해결하기 위해서 스프링 MVC는 약간의 트릭을 사용하는데, 히든 필드를 하나 만들어서, _open처럼 기존 체크 박스 이름 앞에 언더스코어(_)를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다. 히든 필드는 항상 전송된다. 따라서 체크를 해제한 경우 여기에서 open 은 전송되지 않고, _open 만 전송되는데, 이 경우 스프링 MVC는 체크를 해제했다고 판단한다.
체크 해제를 인식하기 위한 히든 필드
<input type="hidden" name="_open" value="on"/>
체크 박스 체크
open=on&_open=on
체크 박스를 체크하면 스프링 MVC가 open 에 값이 있는 것을 확인하고 사용한다. 이때 _open 은 무시한다.
체크 박스 미체크
_open=on
체크 박스를 체크하지 않으면 스프링 MVC가 _open 만 있는 것을 확인하고, open 의 값이 체크되지 않았다고 인식한다.
이 경우 서버에서 Boolean 타입을 찍어보면 결과가 null 이 아니라 false 인 것을 확인할 수 있다.
타임리프가 제공하는 폼 기능을 사용하면 이런 부분을 자동으로 처리할 수 있다.
<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>
타임리프 체크 박스 HTML 생성 결과
<div class="form-check">
<input type="checkbox" id="open" class="form-check-input" name="open" value="true" checked="checked">
<input type="hidden" name="_open" value="on"/>
<label for="open" class="form-check-label">판매 오픈</label>
</div>
타임리프를 사용하면 체크 박스의 히든 필드 부분이 자동으로 생성되어 있다.
또한 조회시에는 checked 속성이 추가된 것을 확인할 수 있다. 타임리프의 th:field 를 사용하면, 값이 true 인 경우 체크를 자동으로 처리해준다. 이런 부분을 개발자가 직접 처리하려면 상당히 번거롭다.
4. 체크박스 - 멀티
@ModelAttribute("regions")
public Map<String, String> regions() {
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
return regions;
}
@ModelAttribute의 특별한 사용법
등록 폼, 상세화면, 수정 폼에서 모두 서울, 부산, 제주라는 체크 박스를 반복해서 보여주어야 한다. 이렇게 하려면 각각의 컨트롤러에서 model.addAttribute(...) 을 사용해서 체크 박스를 구성하는 데이터를 반복해서 넣어주어야 한다.
@ModelAttribute 는 이렇게 컨트롤러에 있는 별도의 메서드에 적용할 수 있다.
이렇게하면 해당 컨트롤러를 요청할 때 regions 에서 반환한 값이 자동으로 모델( model )에 담기게 된다. 물론 이렇게 사용하지 않고, 각각의 컨트롤러 메서드에서 모델에 직접 데이터를 담아서 처리해도 된다.
<div>
<div>등록 지역</div>
<div th:each="region : ${regions}" class="form-check form-check-inline">
<input type="checkbox" th:field="*{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>
</div>
멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다. 그런데 문제는 이렇게 반복해서 HTML 태그를 생성할 때, 생성된 HTML 태그 속성에서 name 은 같아도 되지만, id 는 모두 달라야 한다. 따라서 타임리프는 체크박스를 each루프 안에서 반복해서 만들 때 임의로 1,2,3 숫자를 뒤에 붙여준다.
타임리프 HTML 생성 결과
<div>
<div>등록 지역</div>
<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>
</div>
th:for="${#ids.prev('regions')}" : <label for="id 값"> 에 지정된 id 가 checkbox 에서 동적으로 생성된 regions1 , regions2 , regions3 에 맞추어 순서대로 입력된 것을 확인할 수 있다.
서울, 부산 선택 : item.regions=[SEOUL, BUSAN]
지역 선택X : item.regions=[]
_regions 는 앞서 설명한 기능이다. 웹 브라우저에서 체크를 하나도 하지 않았을 때, 클라이언트가 서버에 아무런 데이터를 보내지 않는 것을 방지한다. 참고로 _regions 조차 보내지 않으면 결과는 null 이 된다.
타임리프의 체크 확인
멀티 체크 박스에서 등록 지역을 선택해서 저장하면, 조회시에 checked 속성이 추가된 것을 확인할 수 있다.
타임리프는 th:field 에 지정한 값과 th:value 의 값을 비교해서 체크를 자동으로 처리해준다.
5. 라디오 버튼
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input">
<label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label">BOOK</label>
</div>
</div>
라디오 버튼은 이미 선택이 되어 있다면, 수정시에도 항상 하나를 선택하도록 되어 있으므로 체크 박스와 달리 별도의 히든 필드를 사용할 필요가 없다.
타임리프로 생성된 HTML
<div>상품 종류</div>
<div class="form-check form-check-inline">
<input type="radio" value="BOOK" class="form-check-input" id="itemType1" name="itemType">
<label for="itemType1" class="form-check-label">도서</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" value="FOOD" class="form-check-input" id="itemType2" name="itemType" checked="checked">
<label for="itemType2" class="form-check-label">식품</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" value="ETC" class="form-check-input" id="itemType3" name="itemType">
<label for="itemType3" class="form-check-label">기타</label>
</div>
</div>
선택한 식품( FOOD )에 checked="checked" 가 적용된 것을 확인할 수 있다.
6. 셀렉트 박스
<div>
<div>배송 방식</div>
<select th:field="*{deliveryCode}" class="form-select"> <option value="">==배송 방식 선택==</option>
<option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}">FAST</option>
</select>
</div>
타임리프로 생성된 HTML
<div>
<DIV>배송 방식</DIV>
<select class="form-select" id="deliveryCode" name="deliveryCode">
<option value="">==배송 방식 선택==</option>
<option value="FAST" selected="selected">빠른 배송</option> <option value="NORMAL">일반 배송</option>
<option value="SLOW">느린 배송</option>
</select>
</div>
selected="selected" 빠른 배송을 선택한 예시인데, 선택된 샐랙트 박스가 유지되는 것을 확인할 수 있다.
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의
웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있
www.inflearn.com
'김영한 강의 요약' 카테고리의 다른 글
김영한 스프링 강의 요약 - 검증(유효성 검사) (0) | 2023.11.23 |
---|---|
스프링 메시지, 국제화 (0) | 2023.11.17 |
타임리프 기본 기능 (0) | 2023.10.23 |
타임리프 간단히 알아보기 (0) | 2023.10.20 |
스프링 요청매핑, HTTP 요청, HTTP 응답 (0) | 2023.10.11 |