본문 바로가기

MVC

[MVC] PRG 패턴

✍ PRG Post/Redirect/Get

웹을 개발하다 상품 등록을 완료하고 웹 브라우저의 새로고침 버튼을 클릭했을 때

상품이 계속해서 중복 등록되는 경험을 해본 적이 있을 것이다.

상품 관리를 하는 웹 프로그램이라고 가정해보자.

 

 

전체 흐름

 

 

 

POST 등록 후 새로 고침

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

 

 

 

 

 

💡 웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.

  • 상품 등록 폼에서 데이터를 입력하고 저장을 선택하면 POST /add + 상품 데이터를 서버로 전송한다.
  • 이 상태에서 새로 고침을 또 선택하면 마지막에 전송한 POST /add + 상품 데이터를 서버로 다시 전송하게 된다.
  • 쉽게 말하면 POST로 요청을 하고 POST로 결과가 온 웹 브라우저에서 새로 고침을 해서 다시 POST 요청을 하는 것! 
  • POST(요청) POST(응답) 새로고침(POST)
  • 그래서 내용은 같고, ID만 다른 상품 데이터가 계속 쌓이게 된다.

 

🤔 이 문제를 어떻게 해결할 수 있을까? 


❝ POST, Redirect GET ❞

 

@PostMapping("/add")
    public String addItemV5(Item item) {
        itemRepository.save(item);
        return "redirect:/basic/items/" + item.getId();
    }

 

💡 웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.

  • 새로 고침 문제를 해결하려면 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해주면 된다.
  • 웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다. 
  • 따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id} 가 되는 것이다.
  • 이후 새로고침을 해도 상품 상세 화면으로 이동하게 되므로 새로 고침 문제를 해결할 수 있다.
  • 이런 문제 해결 방식을 PRG Post/Redirect/Get 라 한다.

 

 

주의 ⚠️

"redirect:/basic/items/" + item.getId() redirect에서 +item.getId()

 

  • URL에 변수를 더해서 사용하는 것은 URL 인코딩이 안되기 때문에 위험하다. 
  • RedirectAttributes 를 사용하자.

RedirectAttributes

상품을 저장하고 상품 상세 화면으로 리다이렉트 한 것 까지는 좋았다. 

그런데 고객 입장에서 저장이 잘 된 것인지 안 된것인지 확신이 들지 않는다. 

그래서 저장이 잘 되었으면 상품 상세 화면에 "저장되었습니다"라는 메시지를 보여달라는 요구사항이 왔다. 

 

🤔 그럼 별도의 화면을 만들어야 할까..?

@PostMapping("/add")
public String addItemV6(Item item, RedirectAttributes redirectAttributes) {
    Item saveItem = itemRepository.save(item);
    // {itemId} 에 치환될 수 있음
    redirectAttributes.addAttribute("itemId", saveItem.getId());
    // ?status=true 쿼리 파라미터 형식으로 들어감
    redirectAttributes.addAttribute("status", true); 
    return "redirect:/basic/items/{itemId}"; 
}
  • 리다이렉트 할 때 간단히 status=true 를 추가해보자.
  • 그리고 뷰 템플릿에서 이 값이 있으면, 저장되었습니다. 라는 메시지를 출력해보자.


실행해보면 다음과 같은 리다이렉트 결과가 나온다.

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


RedirectAttributes

  • RedirectAttributes 를 사용하면 URL 인코딩도 해주고, pathVariable , 쿼리 파라미터까지 처리해준다.
  • redirect:/basic/items/{itemId}
  • pathVariable 바인딩: {itemId}
  • 나머지는 쿼리 파라미터로 처리: ?status=true

 

뷰 템플릿 메시지 추가

<h2 th:if="${param.status}" th:text="'저장 완료'"></h2>
  • th:if : 해당 조건이 참이면 실행
  • ${param.status} : 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능
  • 이런식으로 조건을 줘서 메세지를 쉽게 추가할 수 있다.

 

 

'MVC' 카테고리의 다른 글

[MVC] Tymeleaf 스프링 통합과 폼  (0) 2024.07.25
[MVC] Thymeleaf 기본 기능  (0) 2024.07.24
[MVC] Thymeleaf 뷰 템플릿  (1) 2024.07.22
[MVC] HTTP 응답  (0) 2024.07.21
[MVC] HTTP 요청  (0) 2024.07.21