티스토리 뷰

해당 글은 김영한님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의를 수강하고 정리한 게시글입니다.
PRG Post/Redirect/Get
@PostMapping("/basic/items/add")
public String addItemV4(Item item) {
itemRepository.save(item);
return "basic/item";
}
// model.addAttribute("item", item); 자동 추가, 생략
// 클래스의 첫글자를 소문자로 바꾸고 ModelAttribute에 넣어주기 때문에 생략
// ModelAttribute 또한 생략
위와 같은 기능을하는 컨트롤러가 있고 이 컨트롤러의 기능은 다음과 같다.
[전체 흐름]


상품 등록 페이지에서 상품을 입력하고 상품을 등록한다.

상품 등록이 완료되고 저장되면 상품 상세 페이지를 보여준다.
이 컨트롤러에는 심각한 문제가 있다.
상품 등록을 완료하고 나온 상품 상세 페이지에서 새로고침을 계속 하게되면 다음과 같다.

1번 새로 고침시..

2번 새로 고침시..
새로 고침을 할 때마다 이전에 등록한 상품이 “상품 ID”만 바껴서 계속 등록되는 걸 알 수 있다.
그 원인은 다음 그림을 통해 알 수 있다.

웹 브라우저의 새로 고침은 마지막에 서버에 전송한 데이터를 다시 전송한다.
상품 등록 폼에서 데이터를 입력하고 저장을 선택하면 “POST /add + 상품 데이터”를 서버로 전송한다.
이 상태에서 새로 고침을 또 선택하면 마지막에 전송한 ”POST /add + 상품 데이터”를 서버로 다시 전송하게 된다.
그래서 내용은 같고, ID만 다른 상품 데이터가 계속 쌓이게 된다.
이 문제를 어떻게 해결할 수 있을까?
POST, Redirect GET

위 그림은 이전과 달리 상품 저장 후에 뷰 템플릿으로 이동하는 것이 아니라, 상품 상세 화면으로 리다이렉트를 호출해준다.
웹 브라우저는 리다이렉트의 영향으로 상품 저장 후에 실제 상품 상세 화면으로 다시 이동한다. 따라서 마지막에 호출한 내용이 상품 상세 화면인 GET /items/{id} 이 되는 것이다. (이렇게 새로고침 문제를 해결할 수 있다.)
문제 개선 코드
/**
* PRG - Post/Redirect/Get
*/
@PostMapping("/basic/items/add")
public String addItemV5(Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
이전 코드에서 변경점 : Redirect를 통해 실제 상품 상세 화면으로 이동!
이런 문제 해결 방식을 PRG Post/Redirect/Get 라 한다.
주의 : “redirect:/basic/items/“ + item.getId() redirect에서 +item.getId() 처럼 URL에 변수를 더해서 사용하는 것은 URL 인코딩이 안되기 때문에 위험하다. 다음에 설명하는 RedirectAttributes를 사용하자.
RedirectAttributes
상품을 저장하고 상품 상세 화면으로 리다이렉트 한 것 까지는 좋았다. 그런데 고객 입장에서 저장이 잘 된 것인지 안 된 것인지 확신이 들지 않는다. 그래서 저장이 잘 되었으면 상품 상세 화면에 “저장되었습니다”라는 메시지를 보여달라는 요구사항이 왔다.
간단하게 해결해보자.
/**
* RedirectAttributes
*/
@PostMapping("/basic/items/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}";
}
리다이렉트 할 때 간단히 status=true 를 추가해보자.
그리고 뷰 템플릿에서 이 값이 있으면, “저장되었습니다..“ 라는 메시지를 출력해보자.
실행해보면 다음과 같은 리다이렉트 결과가 나온다.
http://localhost:8080/basic/items/3?status=true
RedirectAttributes
RedirectAttributes 를 사용하면 URL 인코딩도 해주고, pathVarible , 쿼리 파라미터까지 처리해준다.
redirect:/basic/items/{itemId}
- pathVariable 바인딩: {itemId}
- 나머지는 쿼리 파라미터로 처리: ?status=true
뷰 템플릿에 추가할 코드
<!-- 추가 -->
<h2 th:if="${param.status}" th:text="'저장 완료'"></h2>
th:if: 해당 조건이 참이면 실행${param.status}: 타임리프에서 쿼리 파라미터를 편리하게 조회하는 기능- 원래는 컨트롤러에서 모델에 직접 담고 값을 꺼내야 한다. 그런데 쿼리 파라미터는 자주 사용해서 타임리프에서 직접 지원한다.
[결과 화면]

뷰 템플릿에 메시지를 추가하고 실행해보면 위와 같이 “저장 완료!” 라는 메시지가 나오는 것을 확인할 수 있다.
'Backend > Spring' 카테고리의 다른 글
| [Spring MVC] 17. View: 타임리프 기초와 예시 (0) | 2022.04.05 |
|---|---|
| [Spring MVC] 16. HTTP 메시지 컨버터 (0) | 2022.03.28 |
| [Spring MVC] 15. 스프링 MVC - 기본 기능 (0) | 2022.03.28 |
| [Spring MVC] 14. 스프링 MVC 적용하기 (0) | 2022.03.24 |
| [Spring MVC] 13. 스프링 MVC 구조 이해 (0) | 2022.03.23 |