스프링 MVC 활용(13) : 핸들러 메소드 5부 - @ModelAttribute

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

13. 핸들러 메소드 5부 - @ModelAttribute

@ModelAttribute

  • 여러 곳에 있는 단순 타입 데이터를 복합 타입 객체로 받아오거나 해당 객체를 새로 만들 때 사용할 수 있다.
  • 여러 곳? URI 패스, 요청 매개변수, 세션 등
  • 생략 가능
@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")
    @ResponseBody
    public Event createEvent(@ModelAttribute Event event){
        return event;
    }
}
@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{
        mockMvc.perform(post("/events)
                .param("name", "spring")
                .param("limit", "20"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(spring));
    }
}

또는 요청 파라미터 말고도 URI 패턴도 복합 타입 객체로 받아올 수 있다.

@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/name/{name}")
    @ResponseBody
    public Event createEvent(@ModelAttribute Event event){
        return event;
    }
}
@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{
        mockMvc.perform(post("/events/name/spring")
                .param("limit", "20"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(spring));
    }
}

그런데 만약에 limit 파라미터에 Integer가 아닌 String 값을 채운다고 가정해보자.

@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{
        mockMvc.perform(post("/events/name/spring")
                .param("limit", "abc"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(spring));
    }
}

이 경우에는 BindException이 발생한다.

값을 바인딩 할 수 없는 경우에는?

  • BindException 발생 400 에러

바인딩 에러를 직접 다루고 싶은 경우

  • BindingResult 타입의 아규먼트를 바로 오른쪽에 추가한다.

BindingResult를 추가하면 일단 바인딩은 된다. 하지만 BindingResult 안에 에러가 들어있다.

@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/name/{name}")
    @ResponseBody
    public Event createEvent(@ModelAttribute Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            System.out.println("======================");
            bindingResult.getAllErrors().forEach(c -> {
                System.out.println(c.toString());
            });
        }
        return event;
    }
}

바인딩 이후에 검증 작업을 추가로 하고 싶은 경우

  • @Valid 또는 @Validated 애노테이션을 사용한다.
@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{
        mockMvc.perform(post("/events/name/spring")
                .param("limit", "-10"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name").value(spring));
    }
}
public class Event {

    private Integer id;

    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;
    }
}
@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/name/{name}")
    @ResponseBody
    public Event createEvent(@Valid @ModelAttribute Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            System.out.println("======================");
            bindingResult.getAllErrors().forEach(c -> {
                System.out.println(c.toString());
            });
        }
        return event;
    }
}

바인딩 이후 Validation 작업 수행 -> 여기서도 에러가 발생하면 BindingResult에 담아준다.

BindingResult를 추가하면 일단 바인딩은 된다. 하지만 BindingResult 안에 에러가 들어있다.

참고

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

0개의 댓글