MVC 패턴으로 화면을 구성하다 보면 한 가지 문제가 발생한다.
상품을 등록하는 과정을 예로 들어보자.
Client가 /add 경로를 GET방식으로 요청하면 Server는 상품등록 페이지를 Client에게 전달한다. 상품등록 페이지를 받은 Client는 데이터를 입력하고 상품등록 버튼을 누르면 /add 경로를 POST 방식으로 Server에 요청한다. 요청을 받은 Server는 상품등록 비즈니스 로직을 처리하고 그 결과를 담은 상품상세 페이지를 Client에게 전달한다. Client는 등록된 결과를 상품상세 페이지로 전달받으며 마무리된다.
그러나 여기에는 한 가지 문제가 있다.
Client의 마지막 요청은 /add 경로의 Post 방식 요청이다. Client가 브라우저를 새로고침하면 /add 경로의 Post 방식 요청이 다시 Server로 전송된다. 새로 고침을 할 때마다 이미 등록된 상품을 반복하여 등록요청을 보내는 것이다. 이런 오류를 원천 차단하려면 PRG 구조로 요청을 처리하면 된다.
PRG ( Post/Redirect/Get )
클라이언트가 POST 요청을 하면 서버가 Redirect 하는 것이다. Redirect란 서버가 클라이언트에게 특정 경로로 요청하라고 지시를 내리는 것이다. 정리하면 POST 요청이 들어왔을 때, Server과 결과를 응답하는 것이 아니라, 결과가 있는 경로로 Redirect 시키는 것이다.
새로 등록된 상품의 상품상세페이지는 /items/3 경로에 있다. 그러므로 Server는 /items/3 경로로 Redirect 시킨다. Client는 지시대로 /items/3 경로로 Get방식 요청을 한다. 요청을 받은 Server는 상품상세페이지로 응답한다.
그럼 이를 코드로 확인해보자.
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {
private final ItemRepository itemRepository;
// 상품등록 페이지 요청 메서드 ( GET 방식 )
@GetMapping("/add")
public String addForm(){
return "basic/addForm"; // 상품등록 페이지 경로
}
// 상품등록 요청 메서드 ( POST 방식 )
@PostMapping("/add")
public String addItem(Item item, RedirectAttributes redirectAttributes){
Item savedItem = itemRepository.save(item); // 상품 등록 비즈니스 로직
redirectAttributes.addAttribute("itemId",savedItem.getId()); //Redirect 경로 설정
return "redirect:/basic/items/{itemId}"; //Redirect로 응답하기
}
// 상품상세 페이지 요청 메서드 ( GET 방식 )
@GetMapping("/{itemId}")
public String item(@PathVariable("itemId") long itemId, Model model){
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/item"; // 상품상세 페이지 경로
}
}
addForm 메소드는 상품등록 페이지를 Client에게 보내는 메소드이다. 클라이언트가 상품등록 버튼을 클릭하면 addItem 메서드가 실행된다. 메서드는 상품등록 비즈니스 로직을 처리하고 리다이렉트 속성에 리다이렉트 경로를 설정하고 응답한다. Client는 Redirect 지시대로 Get방식 요청을 하고 item 메서드가 실행된다. item 메서드는 Item을 조회하고 상품 상세 페이지를 Client에게 응답한다. 이처럼 서버가 클라이언트를 Redirect 시키면 Post 요청에 머물러 있지 않게 되어 문제를 해결할 수 있다.
참고자료
'SPRING > Spring MVC' 카테고리의 다른 글
[SpringMVC] 검증(Validation)(2) - BindingResult ( FieldError ) (0) | 2023.08.28 |
---|---|
[SpringMVC] 검증(Validation)(1) - HashMap (0) | 2023.08.22 |
[SpringMVC] @ModelAttribute 와 Model (0) | 2023.08.17 |
[SpringMVC] HTTP 메시지 컨버터 동작원리 (0) | 2023.08.15 |
[SpringMVC] HTTP 응답 - Controller 반환타입 (0) | 2023.08.14 |