이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.


POST, Redirect GET

예를 들어, 상품 등록 폼에서 상품을 저장하고, 상품 상세 페이지로 간다고 가정하면, 아래와 같은 그림이 된다. 

@GetMapping("/add") //get
public String addForm() {
    return "basic/addForm";
}

@PostMapping("/add") //post
public String addItem(@ModelAttribute Item item) {
    itemRepository.save(item);
    return "basic/item";
}

상품 등록 폼으로 갈때는 GET 요청으로 가고, 상품 저장을 저장하기 위해서는 POST 요청을 해야한다.

상품 저장이 완료가 되면 상품 상세 페이지가 보이게 되는데, 이때 새로고침을 하면 마지막 요청이었던 POST 요청이 계속 이뤄지게 되면서, 상품 등록이 계속 일어나게 된다.

 

 

새로 고침 문제를 해결하려면 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해주면 된다.

웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다.

따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id} 가 되는 것이다.
이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로 고침 문제를 해결할 수 있다.

@PostMapping("/add") //post
public String addItem(@ModelAttribute Item item) {
    itemRepository.save(item);
    return "redirect:/basic/items/" + item.getId(); //리다이렉트
}

 

이런 문제 해결 방식을 PRG Post/Redirect/Get 라 한다.

 

RedirectAttributes

RedirectAttributes는 스프링 MVC에서 리디렉션을 수행할 때 데이터를 전달하는 메커니즘을 제공한다. 리디렉션이 발생할 때, 기존의 모델 어트리뷰트는 유지되지 않으므로, RedirectAttributes를 사용하여 리디렉션된 페이지로 데이터를 전달할 수 있다.

 

 이 방식은 POST-Redirect-GET 패턴을 구현하는 데 유용하며, 폼 제출 후 새로고침으로 인한 중복 제출을 방지하는 데에도 도움이 된다.

 

RedirectAttributes는 컨트롤러 메소드의 매개변수로 추가할 수 있으며, 리디렉션을 수행하는 메소드에서 이를 사용하여 데이터를 추가한다. 주로 addAttribute와 addFlashAttribute 메소드를 사용한다.

 

  • addAttribute: 이 메소드를 사용하여 추가된 속성은 URL 쿼리 매개변수로 전달된다. 이 방법은 데이터가 URL에 노출되기 때문에, 민감한 정보를 전달하기에는 적합하지 않다. URL에 포함될 수 있는 데이터 크기에는 제한이 있으므로, 작은 데이터에 적합하다.
  • addFlashAttribute: 이 메소드를 사용하여 추가된 속성은 리디렉션 후의 요청에서만 임시로 사용할 수 있으며, 그 이후에는 사라진다. 플래시 속성은 세션을 통해 전달되므로 URL에 노출되지 않고, 데이터 크기 제한에 덜 민감하다. 이 방법은 사용자에게 결과 메시지를 보여주는 등의 용도로 유용하다.
    내부적으로 HTTP 세션을 사용하기 때문에, 세션 지원 환경에서만 사용할 수 있다. 클라이언트가 세션 쿠키를 지원하지 않는 환경이라면 플래시 속성이 제대로 작동하지 않을 수 있다.

 

저장이 잘 되었으면 상품 상세 화면에 "저장되었습니다"라는 메시지를 보여달라는 요구사항이 왔다면 RedirectAttributes로 해결할 수 있다.

@PostMapping("/add") //post
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId());
    redirectAttributes.addAttribute("status", true); //쿼리 파라미터로 전송
    return "redirect:/basic/items/{itemId}";//PathVariable리다이렉트
}

 

RedirectAttributes 를 사용하면 URL 인코딩도 해주고, pathVariable , 쿼리 파라미터까지 처리해준다.

  • redirect:/basic/items/{itemId}
  • pathVariable 바인딩: {itemId}
  • 나머지는 쿼리 파라미터로 처리: ?status=true

RedirectAttribute를 사용하면 view 에 PathVariable 이 자동으로 URL Encoding이 적용이 된다.

RedirectAttribute를 사용하지 않는다면 return "redirect:/basic/items/" + savedItem.getId() 과 같이 문자열을 더하는 방식으로 되는데, 이는 url Encoding이 적용되지 않는다.

@pathVariable 리다이렉트
@PostMapping("/{itemId}/edit") //post
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
    itemRepository.update(itemId, item);
    return "redirect:/basic/items/{itemId}";
}​

 

실행해보면 다음과 같이 url에 쿼리파라미터가 붙어서 리다이렉트 된다.

http://localhost:8080/basic/items/3?status=true

 

이제 뷰 템플릿에서 메시지를 추가해주면 된다.

<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
  • th:if : 해당 조건이 참이면 실행
  • ${param.status} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능