본 게시물은 스스로의 공부를 위한 글입니다.
잘못된 내용이 있으면 댓글로 알려주세요!
th:object
, *{...}
, th:field
3개를 함께 사용하면 <form>
을 편리하게 작성할 수 있다.
th:object
: 커맨드 객체를 지정한다.*{...}
: th:object
에서 선택한 객체에 접근한다.${객체.필드}
와 같다.th:object=${객체명}
+*{필드명}
을 사용하던지, ${객체.필드}
를 사용하던지 선택하면 된다.th:field
: HTML 태그의 id, name, value 속성을 자동으로 만들어준다.th:field
에서 지정한 변수 이름과 같게 만들어진다.<label>
등과 함께 사용시 id가 존재하지 않으면 ide측에서 오류로 간주한다. (타임리프는 렌더링시 만들어주기 때문). 따라서 필요할 시에는 id를 직접 적어야한다.th:field
에서 지정한 변수의 값(model에 담긴 값)을 사용한다.@GetMapping("/add")
public String addForm(Model model) {
model.addAttribute("item", new Item()); //빈 오브젝트를 뷰에 넘겨준다.
return "form/addForm";
}
<form action="item.html" th:object="${item}" method="post">
<label for="itemName">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" placeholder="이름을 입력하세요">
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}" placeholder="가격을 입력하세요">
</form>
위 코드를 렌더링하면 다음과 같이 변한다.
<form action="item.html" method="post">
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" value="" placeholder="이름을 입력하세요">
<label for="price">가격</label>
<input type="text" id="price" name="price" value="" placeholder="가격을 입력하세요">
</form>
<form action="/thymeleaf/result" method="post">
<input type="checkbox" id="open" name="open">
<label for="open">판매 오픈</label>
<button>submit</button>
</form>
위와 같은 form에서 전송하면 어떻게 되는가?
open=on
, boolean으로 받으면 open=true
open
이라는 필드 자체가 서버로 전송되지 않는다.open
필드를 체크하지 않은건지, 진짜로 값이 넘어오지 않은건지 판단할 수 없다.이런 문제를 해결하기 위해 스프링은 히든 필드를 만들어서 체크되지 않으면 open=false
을 받는다.
히든 필드는 다음과 같은 규칙에 따라 만든다.
[수정 코드]
<form action="/thymeleaf/result" method="post">
<input type="checkbox" id="open" name="open">
<input type="hidden" name="_open" value="on"/>
<label for="open">판매 오픈</label>
<button>submit</button>
</form>
스프링 mvc가 하는 로직은 다음과 같다.
주의 해야 할 점
@RequestParam
처럼 값을 받으면 이 처리를 못해준다.@ModelAttribute
등을 사용해서 스프링 mvc가 변환을 해주게하자.히든 필드 생성을 타임리프가 해준다.
th:field
)를 사용하면 렌더링시 자동으로 히든 필드를 만들어준다.[타임리프 적용]
<form action="/thymeleaf" method="post">
<input type="checkbox" id="open" th:field="${shop.open}">
<label for="open" class="form-check-label">판매 오픈</label>
<button>submit</button>
</form>
regions
를 map으로 만들어 model에 담은 후 체크 박스를 원소만큼 만들려고 한다.<form action="/thymeleaf" method="post" th:object="${shop}">
<div th:each="region : ${regions}">
<input type="checkbox" th:field="*{regions}" th:value="${region.key}">
<label th:for="${#ids.prev('regions')}" th:text="${region.value}">서울</label>
</div>
</form>
th:each
를 사용해서 객체의 원소만큼 태그를 생성 할 수 있다.<label>
이다. input 태그와 연결을 해주어야 하는데, 동적으로 input 태그가 생성된다면 id 속성 값을 어떻게 해야할까?ids.prev(...)
을 제공한다. 렌더링시 관련된 input의 id에 맞추어서 자동 생성해준다. (숫자를 뒤에 붙여준다.)<form action="/thymeleaf" method="post">
<div>
<input type="checkbox" value="SEOUL" id="regions1" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions1">서울</label>
</div>
<div>
<input type="checkbox" value="BUSAN" id="regions2" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions2">부산</label>
</div>
<div>
<input type="checkbox" value="JEJU" id="regions3" name="regions">
<input type="hidden" name="_regions" value="on"/>
<label for="regions3">제주</label>
</div>
</form>
<input>
과 <label>
의 위치 관계에 따라 원하는데로 렌더링이 되지 않을 수 있다.ids.prev(...)
또는 ids.next(...)
을 적절하게 사용해야 한다.보통 리스트로 전달하게 되는데, value
와 사용자에게 보여지는 text
가 다르다면 객체로 따로 만들어서 model에 담는다.
예를 들어 다음과 같이 배송 속도 데이터인 DeliveryCode
객체가 있다고 하자.
code
와 사용자에게 보여질 displayName
으로 이루어져 있다.@Data
public class DeliveryCode {
String code;
String displayName;
public DeliveryCode(String code, String displayName) {
this.code = code;
this.displayName = displayName;
}
}
@GetMapping("/thymeleaf")
public String thymeleaf(Model model){
List<DeliveryCode> deliveryCodes = new ArrayList<>();
deliveryCodes.add(new DeliveryCode("FAST", "빠른 배송"));
deliveryCodes.add(new DeliveryCode("NORMAL", "일반 배송"));
deliveryCodes.add(new DeliveryCode("SLOW", "느린 배송"));
model.addAttribute("deliveryCodes", deliveryCodes);
return "thymeleaf";
}
<select field="deliveryCodes">
<option value="">==배송 방식 선택==</option>
<option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}"
th:text="${deliveryCode.displayName}">fast
</option>
</select>
<select field="deliveryCodes">
<option value="">==배송 방식 선택==</option>
<option value="FAST">빠른 배송</option>
<option value="NORMAL">일반 배송</option>
<option value="SLOW">느린 배송</option>
</select>
[사용 전] regions
를 model에 담는 코드가 중복된다.
@GetMapping("/thymeleaf")
public String thymeleaf(Model model){
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
model.addAttribute("regions", regions);
return "thymeleaf";
}
@GetMapping("/thymeleaf/edit")
public String thymeleafEdit(Model model){
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
model.addAttribute("regions", regions);
return "thymeleaf/edit";
}
[사용 후]
@ModelAttribute("regions")
public Map<String, String> regions() {
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
return regions;
}
@GetMapping("/thymeleaf")
public String thymeleaf(Model model){
return "thymeleaf";
}
@GetMapping("/thymeleaf/edit")
public String thymeleafEdit(Model model){
return "thymeleaf/edit";
}
@ModelAttribute(사용할 이름)
이다.인프런의 '스프링 MVC 2편(김영한)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.