1. POST 핸들러의 문제점(새로고침)
보통 form을 통해 사용자가 어떤 POST요청을 보내게 된다면 그것을 처리하는 핸들러에서 POST요청을 처리한 뒤 목록을 보여주는 페이지를 띄워주게 됩니다.
하지만 사용자가 서버로부터 목록을 다시 받아오기 위해서 POST에 해당하는 URL에서 새로고침을 한다면 브라우저에서 경고 메시지를 띄우게 됩니다.
이번 포스팅에서는 위와같은 상황에서 사용자가 올바르게 list목록만을 새로 받아오도록 하는 방법을 알아보도록 하겠습니다.
2. POST Redirect Get(PRG) 패턴
위에서 살펴본 문제를 해결하는 방법으로 POST요청을 Redirect하여 사용자가 새로고침시 GET요청을 보내도록 해주는 패턴이 있습니다. 그 방법을 사용하여 문제를 해결해보도록 하겠습니다.
2.1 원인
위의 상황이 발생하는 원인은 당연하게도 한번 했던 POST 요청에 대해 다시 시도를 하려 해서 브라우저에서 경고메시지를 보내주는 것 입니다. 그렇다면 어떻게 해결해야 할까요? POST요청이 2번 일어나지 않게하면 됩니다..
2.2 예제
문제상황
public class Event {
private String name;
0) (
private Integer age;
public void setName(String name) {
this.name = name;
}
public String getName(){return this.name;}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
dto 클래스는 이전 포스팅에서 보았던 dto와 같습니다.
x
public class HelloController {
"/form") (
public String getForm(Model model){
Event event = new Event();
model.addAttribute("event", event);
return "/events/form";
}
"/events") (
public String createEvents( Event event, BindingResult bindingResult){
// ERROR 출력 및 form.html 재요청
if(bindingResult.hasErrors()){
bindingResult.getAllErrors().forEach(v -> {
System.out.println(v.toString());
});
return "/events/form";
}
// DataBase에 사용자가 보낸 데이터를 저장하는 코드
// ...
//에러가 존재하지 않는 경우 LIST를 보여줍니다.
return "/events/list";
}
}
Controller 클래스도 이전 포스팅과 같습니다.
getForm()
핸들러 메소드는 /form
으로 요청시 /events/form(form.html)
을 응답하게 됩니다.
createEvents()
핸들러 메소드는 사용자가 요청한 값을 검사하고 에러가 존재하면 다시 form.html을 보여주고, 에러가 존재하지 않는 경우 DataBase에 사용자가 보낸 데이터를 저장하는 로직이 있다고 가정하고, event목록을 보여주는 list.html
을 보여주게 됩니다.
<form action="#" th:action="@{/events}" method="post" th:object="${event}">
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Incorrect date</p>
<input type="text" name="name"/>
<input type="text" name="age" />
<input type="submit" value="Create" />
</form>
<body>
LIST
</body>
form.html , list.html도 이전포스팅과 같습니다.
어플리케이션을 시작하고 /form
에 요청을 보내면 위와같은 form을 작성할 수 있는 페이지가 나타납니다. 값을 올바르게 작성(name: String, age: Integer) 한 뒤, create버튼을 클릭합니다.
그러면 handler메소드에서 작성했듯이 list.html
을 사용자에게 보여주게 됩니다.
하지만 문제는 여기서 발생합니다. 사용자가 LIST 목록을 다시 받아오기 위해 새로고침할 경우 /events
로 요청을 다시 보내게 되면서 Database에 데이터를 저장하는 로직을 다시실행하게 될수도 있기 때문에, 브라우저에서 경고를 뱉게 됩니다.
해결(Post Redirect 패턴)
바로 위 상황을 해결하기 위해서 우리는 Redirect를 이용해야 합니다.
"/events") (
public String createEvents( Event event, BindingResult bindingResult){
// ERROR 출력
if(bindingResult.hasErrors()){
bindingResult.getAllErrors().forEach(v -> {
System.out.println(v.toString());
});
return "/events/form";
}
// DataBase에 사용자가 보낸 데이터를 저장하는 코드
// ...
//에러가 존재하지 않는 경우 LIST 요청으로 redirect를 합니다.
return "redirect:/events";
}
"/events") (
public String getEvents(Model model){
// 데이터베이스로부터 저장된 list를 가져와 model에 추가합니다.
return "/events/list";
}
controller를 위와 같이 수정합니다.
createEvents()
createEvents() 핸들러에서는 바로 view를 사용자에게 return하는 것이 아니라 redirect:/events
를 통해서 /events
(GET)으로 재요청을 하도록합니다.
getEvents()
목록을 보여주는 list.html을 사용자에게 반환하는 핸들러 메소드입니다.
어플리케이션을 실행하고 다시 form으로 요청하고, 입력후 create를 클릭합니다.
list.html이 나타납니다. 여기서 새로고침을 합니다. 이번에는 경고 메시지가 나타나지 않는 것을 볼 수 있습니다. redirect
를 이용해서 사용자의 요청을 getEvents()
로 바꾸었기 때문입니다.