Spring Boot – JSTL form validation

By | 2023년 3월 1일
Table of Contents

Spring Boot – JSTL form validation

Spring Boot 기반으로 JSTL form validation 을 하는 방법을 정리합니다.

의존성 추가

dependencies {
    implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'javax.servlet:jstl'
}

Form 클래스

DTO 객체와는 별도로 Form 객체가 필요합니다.

@Getter
@Setter
@NoArgsConstructor
public class ItemForm {
    private Long itemId;
    private Long companyId;
    @NotNull(message = "고객사ID 를 입력하세요.")
    private Long partnerId;
    private String companyName;
    private String partnerName;
    @NotBlank(message = "상품명을 입력하세요.")
    private String itemName;
    private String itemOptionName;
    private Float weight;
}

Controller

itemFormItemForm 와 대응해야 합니다.
폼 클래스명에서 첫글자만 소문자로 변경해서 사용합니다.
이름이 다른 경우 오류 메시지가 표시되지 않습니다.

BindingResult 는 폼 클래스 바로 다음에 위치해야 합니다.

오류가 있는 경우 폼 JSP 로 다시 이동시켜 줍니다.

@Controller
@RequiredArgsConstructor
public class ItemController {
    @GetMapping("/item/create")
    public String create(ModelMap model) {
        model.addAttribute("itemForm", new ItemForm());
        return "/itemModify";
    }

    @PostMapping("/item/create")
    public String doCreate(ModelMap model, @Valid ItemForm form, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            // List<ObjectError> list = bindingResult.getAllErrors();
            // for (ObjectError error : list) {
            //     System.out.println(error);
            // }
            model.addAttribute("itemForm", form);
            return "/itemModify";
        }

        service.create(form);
        return "redirect:/item/";
    }

JSP

오류가 있는 경우 표시해 줍니다.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%--@elvariable id="itemForm" type="kr.pe.skyer9.warehouseweb.external.dto.ItemForm"--%>

<form:form id="frm" name="frm" modelAttribute="itemForm">
<form:input path="itemName" />
<form:errors path="itemName" />
</form:form>

NumberFormatException

NumberFormatException 의 경우 디폴트 오류 메시지가 나옵니다.

Failed to convert property value of type java.lang.String to required type java.lang.Float for property sizeWidth; nested exception is java.lang.NumberFormatException: For input string: "aa"

오류를 한국어로 바꾸기 위해 다음 설정을 해줍니다.

application.yaml

spring:
  messages:
    basename: messages

resources/messages.properties

# typeMismatch.itemForm.sizeWidth=가로 사이즈는 숫자만 입력해야 합니다.
typeMismatch.java.lang.Float=숫자로 입력해야 합니다.

typeMismatch.폼이름.필드명 이 있는 경우 그 오류메시지가 표시됩니다.
대응하는 코드가 없는 경우 typeMismatch.java.lang.Float 와 같이 범용 오류메시지가 표시됩니다.

Form 클래스 분리

입력용 클래스 자체를 분리하는 것이 결과적으로는 제일 간단합니다.

public class UserCreateForm {
}

public class UserModifyForm {
}

public class UserChangePasswordForm {
}

Custom Validator

복합 조건과 같은 특수한 체크를 위해 Custom Validator 를 생성할 수 있습니다.

import org.springframework.validation.Validator;

@Component
public class UserCreateValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return UserCreateForm.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        UserCreateForm dto = (UserCreateForm) target;

        if (ObjectUtils.isEmpty(dto.getCompanyId())) {
            errors.rejectValue("companyId", "required", "업체ID 를 입력하세요.");
        }

        if (!Objects.equals(dto.getUserLoginPassword(), dto.getUserLoginPassword2())) {
            errors.rejectValue("userLoginPassword2", "passwordNotMatch", "비밀번호가 일치하지 않습니다.");
        }
    }
}

userCreateForm 은 검증할 클래스 UserCreateForm 에서 첫글자만 소문자로 변경한 것입니다.

@Controller
@RequiredArgsConstructor
public class AdminUserController {

    private final UserService service;
    private final UserCreateValidator userCreateValidator;

    @InitBinder("userCreateForm")
    public void initBinder(WebDataBinder webDataBinder) {
        webDataBinder.addValidators(userCreateValidator);
    }

    @PostMapping("/admin/user/create")
    public String doCreate(@LoginInfo LoginInfoDto loginInfo, ModelMap model, @Valid UserCreateForm dto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            setAttribute(loginInfo, model);
            model.addAttribute("userCreateForm", dto);
            return "/admin/userModify";
        }
        service.create(loginInfo, dto);
        return "redirect:/admin/user/";
    }
}

답글 남기기