사실 지금까지 진행한 상품 등록 처리 컨트롤러는 심각한 문제가 있다.
상품 등록한 컨트롤러를 보게 되면
@PostMapping("/add")
public String addSaveV4(Item item){
itemRepository.save(item);
return "basic/item";
}
폼에서 Item 객체타입으로 값들이 전달되서 저장되고 ModelAttribute는 안보이지만 model에 담겨 뷰템플릿으로 가는 코드이다. 단순히 한번의 등록으로 끝난다면 문제는 없지만 등록한 페이지에서 새로고침을 하게 되면 문제가 생긴다.
이유는 저장한 뒤 사이트의 주소를 보게 되면 여전히 add에 머물러있게 되고 새로고침을 하게 되면 등록한 주소와 내용 값들이 다시 호출이 되어 새로고침을 누를 때마다 상품이 등록되기 때문이다.
새로고침이란 마지막에 서버에 전송한 요청을 다시 실행하는 것이므로, post 방식의 상품 등록이 계속 요청되는 것이다.
그렇기 때문에 Redirect를 사용하면 된다.
post 방식의 상품등록이 되면 return으로 redirect 등록한 상품의 상세보기 페이지로 이동하게 되면 요청한 매핑의 마지막 값으로 상품 상세보기 페이지가 되면서 새로고침을 해도 중복되는 상품등록이 되지 않는 것이다.
/**
* PRG - Post/Redirect/Get
*/
@PostMapping("/add")
public String addItemV5(Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
※ 주의
> "redirect:/basic/items/" + item.getId() redirect에서 +item.getId() 처럼 URL에 변수를 더해서 사용하는 것은 URL 인코딩이 안되기 때문에 위험하다. 그러므로 RedirectAttributes 를 사용하자.
RedirectAttributes
상품을 저장하고 상품 상세 화면으로 리다이렉트 한 것 까지는 좋았다. 그런데 고객 입장에서 저장이 잘 된 것인지 안 된 것인지 확신이 들지 않는다. 그래서 저장이 잘 되었으면 상품 상세 화면에 "저장되었습니다"라는 메시지를 보여달라는 요구사항이 왔다.
/**
* RedirectAttributes
*/
@PostMapping("/add")
public String addItemV6(Item item, RedirectAttributes redirectAttributes) {
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/basic/items/{itemId}";
}
같은 상품저장 POST매핑의 모습이지만 RedirectAttributes라는게 추가 되었다.
RedirectAttributes로 저장된 값은 return에 redirect의 변수로 사용이 가능하고, 사용되지 않은 등록된 값은 쿼리파라미터로 매핑주소에 추가되어 이동하게 된다.
리다이렉트 할 때 간단히 status=true 를 추가해보자. 그리고 뷰 템플릿에서 이 값이 있으면, 저장되었습니다. 라는 메시지를 출력해보자.
http://localhost:8080/basic/items/3?status=true
redirect:/basic/items/{itemId}
> pathVariable 바인딩: {itemId}
> 나머지는 쿼리 파라미터로 처리: ?status=true
resources/templates/basic/item.html
간단하게 param으로 status의 값이 잇다면 간단한 문구가 보이게 추가해보자.
<div class="container">
<div class="py-5 text-center">
<h2>상품 상세</h2>
</div>
<!-- 추가 -->
<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
맨 아래에 th:if를 통해 param의 status의 값을 확인하고 문구가 보이게 만들어봤다.
th:if : 해당 조건이 참이면 실행
> ${param.status} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능
뷰 템플릿에 메시지를 추가하고 실행해보면 "저장 완료!" 라는 메시지가 나오는 것을 확인할 수 있다. 물론 상품 목록에서 상품 상세로 이동한 경우에는 해당 메시지가 출력되지 않는다.
'spring' 카테고리의 다른 글
타임리프 - 기본 기능 (2) (0) | 2023.04.10 |
---|---|
타임리프 - 기본 기능 (1) (0) | 2023.04.10 |
간단한 사이트 만들기 (0) | 2023.04.05 |
요청 매핑 헨들러 어뎁터 구조 (0) | 2023.04.04 |
HTTP 메시지 컨버터 (0) | 2023.04.04 |