스프링 MVC 활용(26) : 모델 - @ModelAttribute 또 다른 사용법

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

26. 모델 - @ModelAttribute 또 다른 사용법

@ModelAttribute의 다른 용법

  • @RequestMapping을 사용한 핸들러 메소드의 아규먼트에 사용하기
  • @Controller 또는 @ControllerAdvice를 사용한 클래스에서 모델 정보를 초기화 할 때 사용한다.
  • @RequestMapping과 같이 사용하면 해당 메소드에서 리턴하는 객체를 모델에 넣어 준다.
    - RequestToViewNameTranslator

@ModelAttribute 메소드

@ModelAttribute 
public void subjects(Model model) { 
    model.addAttribute("subjects", List.of("study", "seminar", "hobby", "social")); 
}

@Controller
@SessionAttributes("event")
public class EventController {

    @ModelAttribute
    public void categories(Model model){
        model.addAttribute("categories", List.of("study", "seminar", "hobby", "social"));
    }

    @GetMapping("/events/form/name")
    public String eventsFormName(Model model) {
        model.addAttribute("event", new Event());
        // 자동으로 세션에 넣어줌
        return "events/form-name";
    }

    @PostMapping("/events/form/name")
    public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/events/form-name";
        }
        return "redirect:/events/form/limit";
    }
    
    @GetMapping("/events/form/limit")
    public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
        model.addAttribute("event", event);
        // 자동으로 세션에 넣어줌
        return "events/form-limit";
    }
    
    @PostMapping("/events/form/limit")
    public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
        if(bindingResult.hasErrors()){
            return "/events/form-limit";
        }
        sessionStatus.setComplete();
        // 데이터베이스에서 save 했다고 가정
        attributes.addFlashAttribute("newEvent", event);
        return "redirect:/events/list";
    }
    
    @GetMapping("/events/list")
    public String getEvents(Model model, 
                            @SessionAttriubte LocalDateTime visitTime){
        System.out.println(visitTime);
        
        Event spring = new Event();
        spring.setName("spring");
        spring.setLimit(10);
        
        Event newEvent = (Event) model.asMap().get("newEvent");
        
        // 데이터베이스에서 읽어왔다고 가정한다.
        List<Event> eventList = new ArrayList<>();
        eventList.add(spring);
        eventList.add(newEvent);
        model.addAttribute("eventList", eventList);
        return "/events/list";
    }
}

또는 void가 아니라 List를 리턴할 수 있다. 이러한 경우에는 이름을 지정해 주어야 한다.

@Controller
@SessionAttributes("event")
public class EventController {

    @ModelAttribute("categories")
      public List<String> categories(Model model) {
        return List.of("study", "seminar", "hobby", "social");
    }

    @GetMapping("/events/form/name")
    public String eventsFormName(Model model) {
        model.addAttribute("event", new Event());
        // 자동으로 세션에 넣어줌
        return "events/form-name";
    }

    @PostMapping("/events/form/name")
    public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/events/form-name";
        }
        return "redirect:/events/form/limit";
    }
    
    @GetMapping("/events/form/limit")
    public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
        model.addAttribute("event", event);
        // 자동으로 세션에 넣어줌
        return "events/form-limit";
    }
    
    @PostMapping("/events/form/limit")
    public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
        if(bindingResult.hasErrors()){
            return "/events/form-limit";
        }
        sessionStatus.setComplete();
        // 데이터베이스에서 save 했다고 가정
        attributes.addFlashAttribute("newEvent", event);
        return "redirect:/events/list";
    }
    
    @GetMapping("/events/list")
    public String getEvents(Model model, 
                            @SessionAttriubte LocalDateTime visitTime){
        System.out.println(visitTime);
        
        Event spring = new Event();
        spring.setName("spring");
        spring.setLimit(10);
        
        Event newEvent = (Event) model.asMap().get("newEvent");
        
        // 데이터베이스에서 읽어왔다고 가정한다.
        List<Event> eventList = new ArrayList<>();
        eventList.add(spring);
        eventList.add(newEvent);
        model.addAttribute("eventList", eventList);
        return "/events/list";
    }
}

테스트 코드

@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;
    
    @Test
    public void getEvents() throws Exception{
        Event newEvent = new Event();
        newEvent.setName("Winter is coming.");
        newEvent.setLimit(10000);

        mockMvc.perform(get("/events/list")
               .sessionAttr("visitTime", LocalDateTime.now())
               .flashAttr("newEvent", newEvent))
               .andDo(print())
               .andExpect(status().isOk())
               .andExpect(model().attributeExists("categories"))
               // HTML 본문 테스트
               .andExpect(xpath("//p").nodeCount(2));
    }
}

테스트 코드를 돌려보면 model 정보에 categories가 추가된 걸 확인할 수 있다.

또한 @RequestMapping과 같이 사용하면 해당 메소드에서 리턴하는 객체를 모델에 넣어 준다. String이 아닌 객체를 리턴하는 경우에는 RequestToViewNameTranslator가 뷰를 찾아준다.

@Controller
@SessionAttributes("event")
public class EventController {

    @ModelAttribute
    public void categories(Model model){
        model.addAttribute("categories", List.of("study", "seminar", "hobby", "social"));
    }

    @GetMapping("/events/form/name")
    @ModelAttribute	// 생략 가능
    public Event eventsFormName() {
        // 여기서 리턴하는 객체를 model에 담아준다.
        return new Event();
    }

    @PostMapping("/events/form/name")
    public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "/events/form-name";
        }
        return "redirect:/events/form/limit";
    }
    
    @GetMapping("/events/form/limit")
    public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
        model.addAttribute("event", event);
        // 자동으로 세션에 넣어줌
        return "events/form-limit";
    }
    
    @PostMapping("/events/form/limit")
    public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
        if(bindingResult.hasErrors()){
            return "/events/form-limit";
        }
        sessionStatus.setComplete();
        // 데이터베이스에서 save 했다고 가정
        attributes.addFlashAttribute("newEvent", event);
        return "redirect:/events/list";
    }
    
    @GetMapping("/events/list")
    public String getEvents(Model model, 
                            @SessionAttriubte LocalDateTime visitTime){
        System.out.println(visitTime);
        
        Event spring = new Event();
        spring.setName("spring");
        spring.setLimit(10);
        
        Event newEvent = (Event) model.asMap().get("newEvent");
        
        // 데이터베이스에서 읽어왔다고 가정한다.
        List<Event> eventList = new ArrayList<>();
        eventList.add(spring);
        eventList.add(newEvent);
        model.addAttribute("eventList", eventList);
        return "/events/list";
    }
}

참고

  • 인프런 : 스프링 웹 MVC(백기선)
profile
이것저것 관심많은 개발자.
post-custom-banner

0개의 댓글