스프링 MVC 활용(15) : 핸들러 메소드 7부 - 폼 서브밋 에러 처리

de_sj_awa·2021년 7월 4일
0
post-custom-banner

15. 핸들러 메소드 7부 - 폼 서브밋 에러 처리

BindingException이나 @Valid, @Validated 검증에서 발생한 BindingResult 안의 에러를 폼에서 어떻게 처리하는가?

바인딩 에러 발생 시 Model에 담기는 정보

  • Event
  • BindingResult.event
@Controller
public class SampleController {

    @GetMapping("/events/form")
    public String eventsForm(Model model) {
        Event newEvent = new Event();
        newEvent.setLimit(50);
        model.addAttribute("event", newEvent);
        return "events/form";
    }

    @PostMapping("/events")
    public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/events/form";
        }
        return "/events/list";
    }
}
public class Event {

    private Integer id;

    @NotBlank
    private String name;

    @Min(0)
    private Integer limit;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getLimit() {
        return limit;
    }

    public void setLimit(Integer limit) {
        this.limit = limit;
    }
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;
    
    @Test
    public void eventForm() throws Exception{
        mockMvc.perform(get("/events/form"))
                .andDo(print())
                .andExpect(view().name("/events/form"))
                .andExpect(model().attributeExists("event"));
    }
    
	
    @Test
    public void createEvent() throws Exception{
        ResultActions result = mockMvc.perform(post("/events")
                .param("name", "spring)
                .param("limit", "-10"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(model().hasErrors());
        ModelAndView mav = result.andReturn().getModelAndView();
        Map<String, Object> model = mav.getModel();
        System.out.println(model.size());
    }
}

디버거로 실행해보면 model에 Event와 BindingResult.event가 들어있다.

<!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('limit')}" th:errors="*{limit}">Incorrect date</p>
    <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
    <input type="text" title="name" th:field="*{name}"/>
  <input type="text" title="limit" th:field="*{limit}"/>
  <input type="submit" value="Create"/>
</form>
</body>
</html>

타임리프 사용시 바인딩 에러 보여주기

<p th:if="${#fields.hasErrors('limit')}" th:errors="*{limit}">Incorrect date</p>
<!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>

타임리프 목록 보여주기

<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>

이제 이벤트 목록 Model을 만들어서 View에서 사용할 정보를 채워줘야 한다.

@Controller
public class SampleController {

    @GetMapping("/events/form")
    public String eventsForm(Model model) {
        Event newEvent = new Event();
        newEvent.setLimit(50);
        model.addAttribute("event", newEvent);
        return "events/form";
    }

    @PostMapping("/events")
    public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult, Model model){
        if(bindingResult.hasErrors()){
            return "/events/form";
        }
        List<Event> eventList = new ArrayList<>();
        eventList.add(event);
        model.addAttribute("eventList", eventList);
        return "/events/list";
    }
}

그런데 만약에 목록을 갱신하고 싶어 F5 버튼을 누르면 중복 Submit이 발생한다. 이를 방지하기 위한 패턴으로 post + redirect + get 패턴이 있다.

Post / Redirect / Get 패턴

@Controller
public class SampleController {

    @GetMapping("/events/form")
    public String eventsForm(Model model) {
        Event newEvent = new Event();
        newEvent.setLimit(50);
        model.addAttribute("event", newEvent);
        return "events/form";
    }

    @PostMapping("/events")
    public String createEvent(@ModelAttribute @Valid Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/events/form";
        }
		// 데이터베이스에서 save 했다고 가정
        return "redirect:/events/list";
    }
    
    @GetMapping("/events/list")
    public String getEvents(Model model){    
        Event event = new Event();
        event.setName("spring");
        event.setLimit(10);
        // 데이터베이스에서 읽어왔다고 가정한다.
        List<Event> eventList = new ArrayList<>();
        eventList.add(event);
        model.addAttribute("eventList", eventList);
        return "/events/list";
    }
}

참고

profile
이것저것 관심많은 개발자.
post-custom-banner

0개의 댓글