타임리프가 제공하는 입력 폼 기능을 적용해서 기존 프로젝트의 폼 코드를 타임리프가 지원하는 기능을 사용해서 효율적으로 개선한다.
th:object="${item}"
: form에 객체가 연결되는 것을 커맨드 객체라고 한다.
th:field="${item.itemName}"
:
-> id="itemName" name="itemName"
1차 생략 가능
th:field="*{itemName}"
:
-> th:object
소속이라는 뜻의 *을 넣어주면 item도 생략 가능하다.
-> id, name, value 삭제 가능
_open
처럼 기존 체크 박스 이름 앞에 언더스코어( _ )를 붙여서 전송하면 체크를 해제했다고 인식할 수 있다.open
은 전송되지 않고 _open
만 전송되는데, 이 경우 스프링 MVC는 체크를 해제했다고 판단한다.th:field="*{open}"
를 사용하면 히든 필드를 thymeleaf가 만들어줌th:value="${type.name()}"
: enum의 name returnth:text="${type.description}"
: enum의 getDescription을 return주의사항 : 검증할 대상 바로 다음에 와야 한다. 순서가 중요!
@ModelAttribute Item item, 바로 다음에 BingdingResult가 와야 한다.
BindingResult는 Model에 자동으로 포함된다.
th:errors
-> 해당 필드에 오류가 있는 경우에 태그를 출력한다.
-> th:if
의 편의 버전이다.
th:errorclass="field-error"
-> th:field
에서 지정한 필드에 오류가 있으면 class 정보를 추가한다.
딱히 문제는 안되지만 여기서 에러가 났었다.
HTML을 해제해주면 에러가 사라진다.
package com.codingbox.item.domain.web.controller;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.codingbox.item.domain.web.dto.DeliveryCode;
import com.codingbox.item.domain.web.dto.Item;
import com.codingbox.item.domain.web.dto.ItemType;
import com.codingbox.item.domain.web.repository.ItemRepository;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
// @RequiredArgsConstructor :
// final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다.
public class BasicItemController {
private final ItemRepository itemRepository;
/*
* @ModelAttribute :
* Controller를 호출할 때 (어떤 메서드가 호출되던지 간에)
* model에 자동으로 해당 내용이 담기도록 보장된다.
*/
@ModelAttribute("regions")
public Map<String, String> regions(){
// 기존 HashMap : 순서가 보장되지 않는다.
// LinkedHashMap : 순서가 보장된다.
Map<String, String> regions = new LinkedHashMap<>();
regions.put("SEOUL", "서울");
regions.put("BUSAN", "부산");
regions.put("JEJU", "제주");
//model.addAttribute("regions", regions);
return regions;
}
@ModelAttribute("itemTypes")
public ItemType[] itemTypes() {
// enum에 있는 값을 배열로 넘겨준다.
return ItemType.values();
}
@ModelAttribute("deliveryCodes")
public List<DeliveryCode> deliveryCodes(){
List<DeliveryCode> deliveryCodes = new ArrayList<>();
deliveryCodes.add(new DeliveryCode("FAST", "빠른 배송"));
deliveryCodes.add(new DeliveryCode("NORMAL", "일반 배송"));
deliveryCodes.add(new DeliveryCode("SLOW", "느린 배송"));
return deliveryCodes;
}
// @Autowired
// 이렇게 생성자가 딱 1개만 있으면 스프링이 해당 생성자에게
// @Autowired로 의존관계(DI)를 주입해준다.
// public BasicItemController(ItemRepository itemRepository) {
// this.itemRepository = itemRepository;
// }
@GetMapping
public String items(Model model) {
List<Item> items = itemRepository.findAll();
model.addAttribute("items", items);
return "basic/items";
}
@GetMapping("/{itemId}")
// {itemId} : 경로변수, @PathVariable : 경로변수의 값을 추출하려면 이거 사용
public String item(@PathVariable long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
@GetMapping("/add")
public String addForm(Model model) {
model.addAttribute("item", new Item());
return "basic/addForm";
}
/*
* post방식
* /basic/items/add url mapping
* 파라미터 받아오기
* save()
* 상세페이지 조회 -> basic/item.html
*/
// @PostMapping("/add")
public String saveItemV1(Model model,
@RequestParam String itemName,
@RequestParam int price,
@RequestParam Integer quantity) {
Item item = new Item();
item.setItemName(itemName);
item.setPrice(price);
item.setQuantity(quantity);
itemRepository.save(item);
model.addAttribute("item", item);
return "basic/item";
}
// @PostMapping("/add")
public String saveItemV2(Model model,
@ModelAttribute("item") Item item) {
// @ModelAttribute가 대신 해주는 역할
// Item item = new Item();
// item.setItemName(itemName);
// item.setPrice(price);
// item.setQuantity(quantity);
itemRepository.save(item);
model.addAttribute("item", item);
return "basic/item";
}
/*
* @ModelAttribute의 name도 생략 가능
* 즉, model.addAttribute("item", item); 생략 가능
* 생략 시 model에 저장되는 name은 클래스명 첫 글자만 소문자로 등록된다. Item -> item
* ----------------------------------------------------------------
* @ModelAttribute("hello") Item item
* -> model.addAttribute("hello", item);
* -> ("hello")도 생략 가능
* Model model도 생략 가능
*/
// @PostMapping("/add")
public String saveItemV3( // Model model,
@ModelAttribute Item item) {
itemRepository.save(item);
// model.addAttribute("item", item);
return "basic/item";
}
/*
* @ModelAttribute 자체 생략 가능하나 권장하진 않음
*/
// @PostMapping("/add")
public String saveItemV4(Item item) {
itemRepository.save(item);
return "basic/item";
}
// @PostMapping("/add")
public String saveItemV5(Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
// @PostMapping("/add")
public String saveItemV6(Item item, RedirectAttributes redirectAttributes) {
System.out.println("Item.open : " + item.getOpen());
System.out.println("Item.regions : " + item.getRegions());
System.out.println("Item.itemType : " + item.getItemType());
Item savedItem = itemRepository.save(item);
// redirectAttributes.addAttribute("itemId", savedItem);
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/" + item.getId();
}
/*
* BindingResult : Item 객체에 값이 잘 담기지 않을 때 BindingResult 객체에 값이 담기게 된다.
* StringUtils : 값이 있을 경우에는 true 반환하고, 공백이나 Null이 들어올 경우에는 false를 반환하게 된다.
*/
// @PostMapping("/add")
public String saveItemV7(Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if( !StringUtils.hasText( item.getItemName() ) ) {
bindingResult.addError(
// FieldError : field 단위의 error는 spring에서 제공해주는 객체
new FieldError("item", "itemName", "상품 이름은 필수입니다."));
}
if( item.getPrice() == null ||
item.getPrice() < 1000 ||
item.getPrice() > 1000000 ) {
bindingResult.addError(
new FieldError("item", "price", "가격은 1,000,000까지 허용합니다."));
}
if( item.getQuantity() == null ||
item.getQuantity() > 10000) {
bindingResult.addError(
new FieldError("item", "quantity", "수량은 최대 9,999까지 허용됩니다."));
}
// 검증에 실패한다면 다시 입력 폼
if( bindingResult.hasErrors() ) {
System.out.println("error : " + bindingResult);
return "basic/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/" + item.getId();
}
// @PostMapping("/add")
public String saveItemV8(Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if( !StringUtils.hasText( item.getItemName() ) ) {
bindingResult.addError(
new FieldError("item", "itemName", item.getItemName(),
false, null, null, "상품 이름은 필수입니다."));
}
if( item.getPrice() == null ||
item.getPrice() < 1000 ||
item.getPrice() > 1000000 ) {
bindingResult.addError(
new FieldError("item", "price", item.getPrice(),
true, null, null, "가격은 1000 ~ 1,000,000까지 허용합니다."));
}
if( item.getQuantity() == null ||
item.getQuantity() > 10000) {
bindingResult.addError(
new FieldError("item", "quantity", item.getQuantity(),
false, null, null, "수량은 최대 9,999까지 허용됩니다."));
}
// 검증에 실패한다면 다시 입력 폼
if( bindingResult.hasErrors() ) {
System.out.println("error : " + bindingResult);
return "basic/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/" + item.getId();
}
@PostMapping("/add")
public String saveItemV9(Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if( !StringUtils.hasText( item.getItemName() ) ) {
bindingResult.addError(
new FieldError("item", "itemName", item.getItemName(),
false, new String[]{"required.item.itemName"}, null, null));
}
if( item.getPrice() == null ||
item.getPrice() < 1000 ||
item.getPrice() > 1000000 ) {
bindingResult.addError(
new FieldError("item", "price", item.getPrice(),
false, new String[] {"range.item.price"},
new Object[] {1000, 1000000}, null));
}
if( item.getQuantity() == null ||
item.getQuantity() > 10000) {
bindingResult.addError(
new FieldError("item", "quantity", item.getQuantity(),
false, new String[] {"max.item.quantity"},
new Object[] {9999}, null));
}
// 검증에 실패한다면 다시 입력 폼
if( bindingResult.hasErrors() ) {
System.out.println("error : " + bindingResult);
return "basic/addForm";
}
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/" + item.getId();
}
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
// 조회
// model에 담아서 editForm.html에 요청 보내기
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/editForm";
}
/*
* update
* update 후에는 상세페이지로 이동
*/
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
System.out.println("item : " + item.getOpen());
itemRepository.update(itemId, item);
return "redirect:/basic/items/{itemId}"; // forward 방식이 아니고 redirect로
}
// 테스트 데이터 추가
@PostConstruct
public void init() {
System.out.println("초기화 메서드 실행");
itemRepository.save(new Item("testA", 10000, 10));
itemRepository.save(new Item("testB", 20000, 20));
}
// 종료 메서드
@PreDestroy
public void destroy() {
System.out.println("종료 메서드 호출");
}
}
required.default=기본 오류 메시지입니다.
#required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.
package com.codingbox.item.domain.web.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Repository;
import com.codingbox.item.domain.web.dto.Item;
@Repository
public class ItemRepository {
private static final Map<Long, Item> store = new HashMap<>();
private static long sequence = 0L;
// 저장
public Item save(Item item) {
item.setId(++sequence);
store.put(item.getId(), item);
return item;
}
// 아이템 하나 검색
public Item findById(Long id) {
return store.get(id);
}
// 전체항목
public List<Item> findAll(){
return new ArrayList<Item>(store.values());
}
// 아이템 수정
public void update(Long itemId, Item updateParam) {
//item 먼저 찾는다.
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());
}
// store clear
public void clearStore() {
store.clear();
}
}
package com.codingbox.item.domain.web.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
// @AllArgsConstructor : 모든 필드 값을 파라미터로 받는 생성자를 생성
// @NoArgsConstructor : 파라미터가 없는 기본 생성자를 생성
@Data
@AllArgsConstructor
public class DeliveryCode {
/*
* FAST : 빠른 배송
* NORMAL : 일반 배송
* SLOW : 느린 배송
*/
private String code;
private String displayName;
}
package com.codingbox.item.domain.web.dto;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class Item {
private Long id;
private String itemName;
private Integer price; // null일 수 있기 때문에 Integer
private Integer quantity; // null일 수 있기 때문에 Integer
// 기능 추가
private Boolean open; // 판매여부
private List<String> regions; // 등록 지역
private ItemType itemType; // 상품 종류
private String deliveryCode; // 배송 방식
public Item() {
super();
}
public Item(String itemName, Integer price, Integer quantity) {
super();
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
package com.codingbox.item.domain.web.dto;
public enum ItemType {
BOOK("도서"), FOOD("식품"),ETC("기타");
private final String description;
private ItemType(String description){
this.description = description;
}
public String getDescription() {
return description;
}
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
<style>
.container {
max-width: 560px;
}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 등록 폼</h2>
</div>
<h4 class="mb-3">상품 입력</h4>
<!-- th:object="${item}" : 커멘드 객체 -->
<form action="item.html"
th:object="${item}"
th:action method="post"> <!--/ 동일한 url을 가지고 있을 경우 ="@{/basic/items/add}" 생략 가능-->
<div>
<label for="itemName">상품명</label>
<!-- id="itemName" 이것도 생략 가능하나 label의 for="itemName" 와 연결되기 때문에 남겨둔다. -->
<input type="text" id="itemName" th:field="*{itemName}"
th:errorclass="field-error" class="form-control"
placeholder="이름을 입력하세요">
<div class="field-error" th:errors="*{itemName}">상품명 오류</div>
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}"
th:errorclass="field-error" class="form-control"
placeholder="가격을 입력하세요">
<div class="field-error" th:errors="*{price}">가격 오류</div>
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" th:field="*{quantity}"
th:errorclass="field-error" class="form-control"
placeholder="수량을 입력하세요">
<div class="field-error" th:errors="*{quantity}">수량 오류</div>
</div>
<hr class="my-4">
<!--순수 html-->
<!-- <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>-->
<!-- thymeleaf -->
<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>
<!-- multi checkbox -->
<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>
<!-- radio button -->
<div>
<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"></label>
</div>
</div>
<hr class="my-4">
<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>
<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='@{/basic/items}'|"
type="button">
취소
</button>
</div>
</div>
</form>
</div>
<!-- /container -->
<script>
function val(){
if(document.getElementById("itemName").value == ""){
alert("item을 넣어주세요");
return false;
}
}
</script>
</body>
</html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th: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:object="${item}"
th:action method="post"> <!--th:action 메서드 방식만 변경-->
<div>
<label for="id">상품 ID</label>
<input type="text" id="id" th:field="*{id}"
class="form-control" value="1" th:value="|${item.id}|" readonly>
</div>
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}"
class="form-control" value="상품A" th:value="|${item.itemName}|">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}"
class="form-control" value="10000" th:value="|${item.price}|">
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" th:field="*{quantity}"
class="form-control" value="10" th:value="|${item.quantity}|">
</div>
<hr class="my-4">
<!-- thymeleaf -->
<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>
<!-- multi checkbox -->
<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>
<!-- radio button -->
<div>
<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"></label>
</div>
</div>
<hr class="my-4">
<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>
<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='@{/basic/items}'|"
type="button">
취소
</button>
</div>
</div>
</form>
</div>
<!-- /container -->
</body>
</html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th: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>
<!--추가-->
<h2 th:if="${param.status}" th:text="'저장 성공!'"></h2>
<div>
<label for="itemId">상품 ID</label>
<input type="text" id="itemId" name="itemId" class="form-control" value="1"
th:value="${item.id}" readonly>
</div>
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control" value="상품A"
th:value="${item.itemName}" readonly>
</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">
<!-- thymeleaf -->
<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>
<!-- multi checkbox -->
<div>
<div>등록 지역</div>
<div th:each="region : ${regions}" class="form-check form-check-inline">
<input type="checkbox" th:field="${item.regions}" th:value="${region.key}"
class="form-check-input" disabled>
<label th:for="${#ids.prev('regions')}" th:text="${region.value}"
class="form-check-label">서울</label>
</div>
</div>
<!-- radio button -->
<div>
<div>상품 종류</div>
<div th:each="type : ${itemTypes}" class="form-check form-check-inline">
<input type="radio"
th:field="${item.itemType}" th:value="${type.name()}"
class="form-check-input" disabled>
<label th:for="${#ids.prev('itemType')}"
th:text="${type.description}"
class="form-check-label"></label>
</div>
</div>
<hr class="my-4">
<div>
<div>배송 방식</div>
<select th:field="${item.deliveryCode}" class="form-select" disabled>
<option value="">==배송 방식 선택==</option><!-- 아무것도 선택 안할때 -->
<option th:each="deliveryCode : ${deliveryCodes}"
th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}">
FAST
</option>
</select>
</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='@{/basic/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='@{/basic/items}'|"
type="button">
목록으로
</button>
</div>
</div>
</div>
<!-- /container -->
<script th:inline="javascript">
if([[${param.status}]] != null){
if([[${param.status}]]){
alert("저장완료");
}else{
alert("저장실패");
}
}
</script>
</body>
</html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>상품 목록</h2>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">
상품 등록
</button>
</div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>상품명</th>
<th>가격</th>
<th>수량</th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${items}">
<td>
<a href="item.html"
th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
th:text="${item.id}">아이템id</a>
</td>
<td>
<a href="item.html"
th:href="@{|/basic/items/${item.id}|}"
th:text="${item.itemName}">상품명</a>
</td>
<td th:text="${item.price}">1000</td>
<td th:text="${item.quantity}">10</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- /container -->
</body>
</html>
package com.codingbox.item.test.enums;
public enum Week {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
// enum 타입은 일반 메서드를 가질 수 있다.
public void dayInfo() {
System.out.println("dayInfo enum");
}
}
package com.codingbox.item.test.enums;
public class Main01 {
public static void main(String[] args) {
Week today = Week.MONDAY;
System.out.println(today);
Week.MONDAY.dayInfo();
Week.FRIDAY.dayInfo();
}
}
package com.codingbox.item.test.enums;
public enum Season {
SPRING, SUMMER, FALL, WINTER;
}
package com.codingbox.item.test.enums;
public class Main02 {
public static void main(String[] args) {
Season season = Season.SPRING;
// name() : 열거 객체의 문자열 리턴
String name = season.name();
System.out.println(name);
System.out.println("--------------------");
// ordinal() : 열거 객체가 몇 번째인지를 리턴
int ordinal = season.ordinal();
System.out.println(ordinal);
System.out.println("--------------------");
// values() : 열거 타입의 모든 열거 객체들을 배열로 만들어 리턴
Season[] season1 = Season.values();
for(Season s : season1) {
System.out.println(s);
}
}
}
package com.codingbox.item.test.enums;
public enum Type {
WALKING("워킹화"),
RUNNING("런닝화"),
TRACKING("트래킹화"),
HIKING("등산화");
final private String name;
private Type(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
package com.codingbox.item.test.enums;
public class Main03 {
public static void main(String[] args) {
for( Type type : Type.values() ) {
System.out.println(type.getName());
}
}
}