Item 상세
@PathVariable
URI에 변수가 들어가는 경우 처리해줄 때 사용하는 어노테이션이다.
// 아이템 상세
@GetMapping("/{itemId}")
public String item(@PathVariable long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
Item 등록
메서드를 통해 기능을 구분지었다. (@GetMapping, @PostMapping)
// 아이템 등록
@GetMapping("/add")
public String addForm() {
return "basic/addForm";
}
// 아이템 저장
@PostMapping("/add")
public String save() {
return "basic/addForm";
}
@RequestParam을 통한 View 데이터 전달
다음은 RequestParameter를 통해 뷰로 전달할 model에 데이터를 추가하고, itemRepository의 save 메서드를 호출하여 메모리에 저장하는 과정이다.
@PostMapping("/add")
public String addItemV1(
@RequestParam String itemName,
@RequestParam int price,
@RequestParam Integer quantity,
Model model
) {
Item item = new Item();
item.setItemName(itemName);
item.setPrice(price);
item.setQuantity(quantity);
itemRepository.save(item);
// 상세 화면에서 저장된 결과를 보여줘야 한다 -> 뷰를 또 만들 필요 없다
model.addAttribute("item", item);
return "basic/item";
}
@ModelAttribute를 통한 View 데이터 전달
@ModelAttribute는 Item 객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법으로 입력한다.
Model에 데이터를 담을 때는 이름이 필요하다. @ModelAttribute를 사용하면 데이터의 이름은 name 속성("item")으로 데이터를 자동으로 넣어주기 때문에 model.addAttribute를 생략할 수 있다.
@PostMapping("/add")
public String addItemV2(@ModelAttribute("item") Item item /*, Model model */) {
// ModelAttribute를 사용하면 set을 호출한다.
// Item item = new Item();
// item.setItemName(itemName);
// item.setPrice(price);
// item.setQuantity(quantity);
itemRepository.save(item);
// model.addAttribute("item", item); // 자동 추가, 생략 가능
return "basic/item";
}
만약 name 속성조차 생략하면 어떻게 될까? Spring에서는 다음 인자인 '클래스'(Item)에서 첫 글자만 소문자로 변경한 것(item)에 데이터를 넣어준다. (Item → item)
@PostMapping("/add")
public String addItemV2(@ModelAttribute Item item) {
itemRepository.save(item);
return "basic/item";
}
@ModelAttribute를 생략할 수도 있다. String이나 Int 등 단순 타입들이 오면 @RequestParam이 적용되지만, 우리가 만든 임의의 객체(Item)과 같은 경우 @ModelAttribute가 적용된다. 하지만 코드 가독성이 매우 낮아지니 유의하자.
@PostMapping("/add")
public String addItemV2(Item item) {
itemRepository.save(item);
return "basic/item";
}
Item 수정
# Redirect
상품 수정의 POST 과정에서 마지막에 뷰 템플릿을 호출하는 대신에 상품 상세 화면으로 이동하도록 리다이렉트를 호출한다.
컨트롤러에 매핑된 @PathVariable의 값을 redirect에 그대로 사용할 수 있다.
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/editForm";
}
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
itemRepository.update(itemId, item);
return "redirect:/basic/items/{itemId}"
}
Item 중복 생성 방지 처리(PRG _ POST/Redirect/GET)
상품 등록 폼을 @GetMapping으로 요청하면 addForm.html을 반환 받게 되고, @PostMapping으로 item add를 수행하면 상품 상세 뷰를 반환하게 된다.
웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송하는 것이다. 즉, POST /add + "new item" 한번 더 실행하게 되는 것이기 때문에 다음 그림과 같이 item이 중복되어서 생성된다.
이 문제는 Redirect로 해결할 수 있다. Redirect는 웹 브라우저 입장에서 새로운 요청을 처리하는 것이다.
상품 저장 후에 실제 상품 상세 화면으로 다시 이동하도록 리다이렉트를 설정하게 되면, 브라우저 입장에선 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id}인 것이므로 새로고침을 해도 GET으로 처리된다.
@PostMapping("/add")
public String addItemV5(@ModelAttribute Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
응답 코드도 302이며, /basic/items/3 주소로 리다이렉트 되었다. 이러한 문제 해결 방식을 PRG Post/Redirect/Get이라고 한다.
RedirectAttributes
RedirectAttributes를 사용하여 저장 후 "저장되었습니다"라는 메시지를 보여주는 기능을 추가하고자 한다. 저장되었음을 체크하기 위해 status를 넣었다.
@PostMapping("/add")
public String addItemV6(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/{itemId}";
}
# RedirectAttributes
URL 인코딩도 해주고, PathVariable, 쿼리 파라미터까지 처리한다.
PathVariable 바인딩은 {itemId}로, 나머지는 쿼리 파라미터로 처리된다.
이제 요청은 /basic/items/{itemId}로 이동한다. 이때 반환되는 뷰 템플릿은 item.html이므로 status가 true일 경우에 대한 처리를 item.html에 추가해야 한다.
// 아이템 상세
@GetMapping("/{itemId}")
public String item(@PathVariable long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
th:if 구문: 해당 조건이 참이면 실행된다.
${param.status}: 타임 리프에서 쿼리 파라미터를 편리하게 조회하는 기능이다.
...
<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>
...
'🌱 Spring > MVC ①' 카테고리의 다른 글
[Spring] Thymeleaf View로 변경 (0) | 2023.07.27 |
---|---|
[Spring] Item 도메인 개발 (0) | 2023.07.27 |
[Spring] HTTP Message Converter (0) | 2023.07.24 |
[Spring] HTTP 응답 _ HTTP API, 메시지 바디에 직접 입력 (0) | 2023.07.24 |
[Spring] HTTP 응답 _ 정적 리소스, 뷰 템플릿 (0) | 2023.07.24 |