상황
사용자가 상품을 구매한 상황. 이 때 사용자의 마지막 요청은 POST로 상품 구매 요청이다. 이 상태에서 그대로 사용자가 새로고침 버튼을 누르게 되면 이전의 요청이 그대로 나가서 물건을 한번 더 구매하는 상황이 발생하게 된다. 이를 방지하기 위한 것이 PRG 이다.
PRG
(1) | 구매자가 상품을 구매하는 화면에서 주문 버튼을 클릭한다. POST메소드를 사용한 요청이 전송된다. |
(2) | 서버는(1)의 요청에서 받은 상품 구매 처리를 DB에 반영한다. |
(3) | 서버는 응답값으로 리다이렉트를 하도록 해서 상품 구매 완료 화면을 표시한다. |
(4) | 리다이렉트에 의해 브라우저가 상품 구매 완료 화면을 표시하도록 서버에 요청을 보낸다. 이때 요청는 GET메소드를 사용한다. |
(5) | 서버가 상품 구매 완료 화면을 보내준다. |
(6) | 사용자가 브라우저에서 새로고침을 누른다. 새로고침에 의해서 서버로 마지막 요청이 다시 나가는데 이때 요청은 상품 구매 완료 화면을 표시하기 위한 GET 요청이기 때문에 상품 구매가 중복으로 발생하지 않는다. |
(7) | 서버는 상품 구매 완료 화면을 전달한다. |
스프링에서는 PRG를 다음과 같이 구현할 수 있다.
@PostMapping("/add")
public String Add(@ModelAttribute Item item) {
repository.save(item);
return "redirect:/basic/items" + item.getId();
}
위 방법에는 단점이 있다. 만약 getId에서 한글이나 공백이 들어오면 인코딩되어야 하는데 위의 방식은 인코딩이 되지 않는다. 그래서 RedicrectAttributes 를 사용해야한다.
@PostMapping("/add")
public String addForm(@ModelAttribute item i, Model model, RedirectAttributes ra) {
item saved = ir.save(i);
ra.addAttribute("itemId", saved.getId());
ra.addAttribute("status", true);
rA.addAttribute("hello", "hihihi");
return "redirect:/basic/items/{itemId}";
}
return 의 redirect 경로에 {변수명} 이 있다면 RedirectAttributes에 해당 변수가 있는지 확인한다. 그래서 실제로 리다이렉트 되는 경로는 saved.getId()로 id값을 가져와서 /basic/items/가져온ID값 이 될 것이다. 나머지는 쿼리 파라미터 형식으로 뒤에 붙는다.
?status=true로 해서 뒤에 붙은 것을 확인할 수 있다.
'백엔드 > 스프링' 카테고리의 다른 글
타임리프 사용법 정리 (0) | 2021.09.13 |
---|---|
스프링 No serializer found for class 에러 해결 (0) | 2021.08.24 |
스프링 MVC - 응답데이터와 메시지 컨버터 (0) | 2021.08.19 |
스프링 MVC - 요청 데이터 (0) | 2021.08.19 |
스프링MVC 요청 매핑 (0) | 2021.08.18 |