BindingResult를 이용해서 Validation을 구현하는 것을 온라인 도서관 프로젝트에 적용해보려는 시도 중에, 문제가 생겼다.
@ModelAttribute
@PostMapping("/login")
public String login(@ModelAttribute User user, Model model) {
model.addAttribute("user", user);
return "home/login";
}
<form action="login" th:action method="post" th:object="${user}">
<legend th:text="#{menu.login}">로그인</legend>
<div class="mb-3">
<label for="username" class="form-label" th:text="#{user.username}">username</label>
<input type="text" class="form-control" th:field="*{username}" id="username">
</div>
<div class="mb-3">
<label for="password" class="form-label" th:text="#{user.password}">Password</label>
<input type="password" class="form-control" th:field="*{password}" id="password">
</div>
<button type="submit" class="btn btn-primary" th:text="#{menu.login}">Login</button>
<a href="join" class="btn btn-dark" th:text="#{menu.join}">Join</a>
</form>
이 코드의 결과는 어떻게 될까? 받은 그대로 다시 폼에 값을 전달해서 넣어준다. 하지만, 사실 직접 model 객체에 다시 Login을 넣어주지는 않아도 된다. ModelAttribute는 자동으로 Model에 넣어 준다.
넣어주지 않는 문제
User가 아닌, dto 객체를 사용하려고 하자 문제가 생겼다. thymeleaf에서 값을 찾아오지 못하는 문제가 있었다.
@PostMapping("/login")
public String login(HttpSession session, @ModelAttribute LoginUserDto user, BindingResult bindingResult) throws IOException {
User loginUser = userService.login(user);
log.debug("Login User: {}", loginUser);
if (loginUser == null) {
bindingResult.addError(new ObjectError("user", "로그인 실패"));
}
if (bindingResult.hasErrors()) {
log.debug("errors={}", bindingResult);
return "home/login";
}
session.setAttribute("user", loginUser);
// 로그인 후에 홈으로 리다이렉트
return "redirect:/";
}
로그 확인
BindingResult로 Validation을 구현하려고 하고 있어서, 마침 바인딩된 오브젝트 이름을 확인할 수 있었는데, 바인딩된 오브젝트 이름은 user가 아닌 loginUserDto로 나오고 있는 게 아닌가!
@PostMapping("/login")
public String login(HttpSession session, @ModelAttribute LoginUserDto user, BindingResult bindingResult) throws IOException {
log.debug("objectName={}", bindingResult.getObjectName()); // loginUserDto로 출력
log.debug("target={}", bindingResult.getTarget()); // 정상적으로 LoginUserDto 인스턴스를 찾아옴.
log.debug("Input User DTO: {}", user);
User loginUser = userService.login(user);
log.debug("Login User: {}", loginUser);
if (loginUser == null) {
bindingResult.addError(new ObjectError("user", "로그인 실패"));
}
if (bindingResult.hasErrors()) {
log.debug("errors={}", bindingResult);
return "home/login";
}
session.setAttribute("user", loginUser);
// 로그인 후에 홈으로 리다이렉트
return "redirect:/";
}
해결 방법
해결 방법은 매우 간단했다. ModelAttribute에 이름을 명시해주면 된다.
@PostMapping("/login")
public String login(HttpSession session, @ModelAttribute("user") LoginUserDto user, BindingResult bindingResult) throws IOException {
log.debug("objectName={}", bindingResult.getObjectName()); // loginUserDto로 나오고 있었다. @ModelAttribute("user")로 해결
log.debug("target={}", bindingResult.getTarget()); // 정상적으로 LoginUserDto 인스턴스를 찾아옴.
log.debug("Input User DTO: {}", user);
User loginUser = userService.login(user);
log.debug("Login User: {}", loginUser);
if (loginUser == null) {
bindingResult.addError(new ObjectError("user", "로그인 실패"));
}
if (bindingResult.hasErrors()) {
log.debug("errors={}", bindingResult);
return "home/login";
}
session.setAttribute("user", loginUser);
// 로그인 후에 홈으로 리다이렉트
return "redirect:/";
}