스프링 웹 MVC-19.폼 서브밋(에러 처리), @SessionAttributes, 멀티 폼 서브밋(핸들러 메소드)
핸들러 메소드 7부: 폼 서브밋 (에러 처리)
바인딩 에러 발생 시 Model에 담기는 정보
- Event
- BindingResult.event
타임리프 사용시 바인딩 에러 보여주기
- https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#field-errors
<p th:if="${#fields.hasErrors('limit')}" th:errors="*{limit}">Incorrect date</p>
Post / Redirect / Get 패턴
- https://en.wikipedia.org/wiki/Post/Redirect/Get
- Post 이후에 브라우저를 리프래시 하더라도 폼 서브밋이 발생하지 않도록 하는 패턴
타임리프 목록 보여주기
- https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#listing-seed-starter-data
실습
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create new event</title>
</head>
<body>
<form action="#" th:action="@{/events}" method="post" th:object="${event}">
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}">에러 있으면 벨리데이트 에러메세지 출력</p>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">에러 있으면 벨리데이트 에러메세지 출력</p>
<input type="text" title="name" th:field="*{name}"/>
<input type="text" title="age" th:field="*{age}"/>
<input type="submit" value="create">
</form>
</body>
</html>
@PostMapping(value = "/events")
public String createEvent(@Validated @ModelAttribute Event event,
BindingResult bindingResult,
Model model) {
if(bindingResult.hasErrors()){
return "/events/form";
}
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute(eventList);
// return "/events/list";
// post 후 새로고침을 하게 되면, 브라우저가 기존 작업을 다시 하는 지 체크(입력 정보를 다시 post 하겠냐? 체크)
// 해결방안으로 post 한 다음 get 리다이렉트를 해 준다.
return "redirect:/events/list"; // 중복 submit 발생하지 않음
}
@GetMapping("/events/list")
public String getEvents(Model model){
// db의 리스트를 꺼내온다고 가정.
Event event = new Event();
event.setName("jaeuk");
event.setAge(30);
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute(eventList);
return "/events/list";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Event List</title>
</head>
<body>
<a th:href="@{/events/form}">Create New Event</a>
<div th:unless="${#lists.isEmpty(eventList)}">
<ul th:each="event: ${eventList}">
<p th:text="${event.Name}">Event Name</p>
</ul>
</div>
</body>
</html>
핸들러 메소드 8부: @SessionAttributes
모델 정보를 HTTP 세션에 저장해주는 애노테이션
- HttpSession을 직접 사용할 수도 있지만
- 이 애노테이션에 설정한 이름에 해당하는 모델 정보를 자동으로 세션에 넣어준다.
- @ModelAttribute는 세션에 있는 데이터도 바인딩한다.
- 여러 화면(또는 요청)에서 사용해야 하는 객체를 공유할 때 사용한다.
ex. 장바구니, 또는 입력받는 정보가 너무 많은 경우 form을 나누는 경우.
첫번째 form에서 입력한 개인정보, 두번째 form에서 입력한 주소정보, 세번째 form에서 입력한 기타정보 등등..
다음 form 으로 가기 전에, 이미 입력한 정보를 세션에 담아둔다.
SessionStatus를 사용해서 세션 처리 완료를 알려줄 수 있다.
- 폼 처리 끝나고 세션을 비울 때 사용한다.
HttpSession을 사용하는 경우
@GetMapping("/events/form")
public String eventsForm(Model model, HttpSession httpSession){
Event event = new Event();
event.setName("jaeuk");
model.addAttribute("event", event);
httpSession.setAttribute("event", event);
return "events/form";
}
@Test
public void eventForm() throws Exception {
mockMvc.perform(get("/events/form"))
.andDo(print())
.andExpect(view().name("events/form"))
.andExpect(model().attributeExists("event"))
.andExpect(request().sessionAttribute("event", notNullValue()))
;
}
@SessionAttributes 사용하는 경우
@Controller
@SessionAttributes("event") // model.addAttribute하는 것 중에 이 이름에 해당하는 것 세션에 자동으로 넣어 줌.
public class SampleController {
@GetMapping("/events/form")
public String eventsForm(Model model, HttpSession httpSession){
Event event = new Event();
event.setName("jaeuk");
model.addAttribute("event", event); // @SessionAttributes("event")이 세션에 넣어줌.
// httpSession.setAttribute("event", event); // 없어도 됨.
return "events/form";
}
@PostMapping(value = "/events")
public String createEvent(@Validated @ModelAttribute Event event,
BindingResult bindingResult,
SessionStatus sessionStatus) {
if(bindingResult.hasErrors()){
return "/events/form";
}
// DB 저장
sessionStatus.setComplete(); // form 처리가 끝났을 때 세션이 만료되도록. 세션 데이터를 전부 비우도록
return "redirect:/events/list";
}
핸들러 메소드 9부: 멀티 폼 서브밋
세션을 사용해서 여러 폼에 걸쳐 데이터를 나눠 입력 받고 저장하기
- 이름 입력받고
- 나이 입력받고
- 서브밋 -> 목록으로!
완료된 경우에 세션에서 모델 객체 제거하기
- SessionStatus
@Controller
//@SessionAttributes("event") // model.addAttribute하는 것 중에 이 이름에 해당하는 것 세션에 자동으로 넣어 줌.
@SessionAttributes(value = {"event", "person"})
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model){
model.addAttribute("event", new Event());
return "events/form-name";
}
@PostMapping(value = "/events/form/name")
public String eventsFormNameSubmit(@Validated @ModelAttribute Event event,
BindingResult bindingResult) {
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/age";
}
@GetMapping("/events/form/age")
public String eventsFormAge(@ModelAttribute Event event, Model model){
model.addAttribute("event", event);
return "events/form-age";
}
@PostMapping(value = "/events/form/age")
public String eventsFormAgeSubmit(@Validated @ModelAttribute Event event,
BindingResult bindingResult,
SessionStatus sessionStatus) {
if(bindingResult.hasErrors()){
return "/events/form-age";
}
// DB 저장
sessionStatus.setComplete();
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model){
// db의 리스트를 꺼내온다고 가정.
Event event = new Event();
event.setName("jaeuk");
event.setAge(30);
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute(eventList);
return "/events/list";
}
}
form-age , -name도 동일하게 구현
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Create new event</title>
</head>
<body>
<form action="#" th:action="@{/events/form/age}" method="post" th:object="${event}">
<p th:if="${#fields.hasErrors('age')}" th:errors="*{age}">에러 있으면 벨리데이트 에러메세지 출력</p>
Age : <input type="text" title="age" th:field="*{age}"/>
<input type="submit" value="create">
</form>
</body>
</html>