스프링 MVC 활용(27) : DataBinder - @InitBinder

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

27. DataBinder - @InitBinder

특정 컨트롤러에서 바인딩 또는 검증 설정을 변경하고 싶을 때 사용

@InitBinder 
public void initEventBinder(WebDataBinder webDataBinder) { 
    webDataBinder.setDisallowedFields("id"); 
}

바인딩 설정

  • webDataBinder.setDisallowedFields();

포매터 설정

  • webDataBinder.addCustomFormatter();

Validator 설정

  • webDataBinder.addValidators();

특정 모델 객체에만 바인딩 또는 Validator 설정을 적용하고 싶은 경우

  • @InitBinder(“event”)

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

    @InitBinder
    public void initEventBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisallowedFields("id");
    }

    @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";
    }
}

webDataBinder.setDisallowedFields("id");라고 쓰면 받고 싶지 않은 필드 값을 걸러낼 수 있다. id는 이벤트를 저장할 때 생성하고 싶기 때문에 쿼리 파라미터나 패스, 폼 등으로 받아오고 싶지 않을 때 이렇게 사용하면 id가 입력되었을 때 걸러낼 수 있다.

public class Event {

    private Integer id;

    @NotBlank
    private String name;

    @Min(0)
    private Integer limit;

    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
//  @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;

    public Integer getId() {
        return id;
    }

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

    public LocalDate getStartDate() {
        return startDate;
    }

    public void setStartDate(LocalDate startDate) {
        this.startDate = startDate;
    }

    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;
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="#" th:action="@{/events/form/name}" method="post" th:object="${event}">
  <p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
  startDate: <input type="text" title="startDate" th:field="*{startDate}"/>
  Name : <input type="text" title="name" th:field="*{name}"/>
  <input type="submit" value="Create"/>
</form>
</body>
</html>

Formatter가 기본으로 등록되어 있어서 String이 LocalDate로 자동으로 바인딩된다.

또한 특정한 값을 이름으로 받고 싶지 않다면 Validator를 등록해주면 된다.

public class EventValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return Event.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        Event event = (Event) target;
        if (event.getName().equalsIgnoreCase("aaa")){
            errors.rejectValue("name", "wrongValue", "the value is not allowed");
        }
    }
}

Validator를 @InitBinder에 등록해준다.

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

    @InitBinder
    public void initEventBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisallowedFields("id");
        webDataBinder.addValidators(new EventValidator());
    }

    @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";
    }
}

아니면 Validator를 @InitBinder에 등록하지 않고, Validator를 빈으로 등록해서 사용해도 된다.

@Component
public class EventValidator {

    public void validate(Event event, Errors errors) {
        if (event.getName().equalsIgnoreCase("aaa")){
            errors.rejectValue("name", "wrongValue", "the value is not allowed");
       }
    }
}
@Controller
@SessionAttributes("event")
public class EventController {

    @Autowired
    EventValidator eventValidator;

    @InitBinder
    public void initEventBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisallowedFields("id");
    }

    @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()){
        // 스프링이 기본적으로 제공하는 Validation Error  검증
            return "/events/form-name";
        }
        eventValidator.validate(event, bindingResult);
        // 원하는 시점에 빈으로 등록한 Validator가 검증
        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";
    }
}

또한 특정 모델 객체에만 바인딩 또는 Validator 설정을 적용하고 싶은 경우 이름을 @InitBinder에 이름을 지정하면 된다.

@InitBinder("event")
public void initEventBinder(WebDataBinder webDataBinder){
    webDataBinder.setDisallowedFields("id");
    webDataBinder.addValidators(new EventValidator());
}

참고

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

0개의 댓글