본문 바로가기
MVC with 김영한

Validation

by wonseok99 2021. 12. 6.

어떠한 사이트를 만들었을 때, 관리 시스템에 검증 로직은 반드시 필요하다!

 

클라이언트 검증, 서버 검증

클라이언트 검증(JS)은 사용자가 조작의 가능성이 있으므로 보안에 취약하다. 서버 검증은 서버로 데이터를 전달받고 검증 결과를 리턴하기 때문에 즉각적인 고객 사용성이 부족하다!

둘을 적절히 잘 섞어서 사용하되, 최종적으로 서버 검증은 필수이다.

 

 

검증 처리 과정

상품 등록과정을 예로들어, 

1.  상품 등록 폼에 등록하기 위한 폼을(GET/ add)을 컨트롤러에 전달하여 상품 등록 폼(addForm.html)을 가져온다.

2. 상품을 등록할(POST/ add) 을 컨트롤러에 전달한 뒤, Model을 검증한다. 

3. 검증이 실패하였다면 검증 오류 결과가 포함된 상품 등록 폼(addForm.html)을 다시 반환한다.

 

 

thymeleaf와 spring의 기능을 활용한 validation - BindingResult

 

- BindingResult

BindingResult가 있다면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다.
보통 입력에서 타입오류가 발생하게 되면 컨트롤러를 호출하지 않고 화이트라벨 오류 페이지로 이동하게 된다.

 

- BindingResult에 검증 오류를 적용하는 3가지 방법

1. @ModelAttribute의 객체에 타입 오류 등으로 바인딩이 실패하는 경우, 스프링이 FieldError를 생성하여 BindingResult에 할당한다.

2. 개발자가 컨트롤러에 조건을 통해 따로 할당할 수 있다.

3. Validatior를 사용한다.

 

 

STEP 1.  Controller 수정


필드 오류

if (!StringUtils.hasText(item.getItemName())) {
    bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다."));
}

 

필드 오류가 있을경우, FieldError 객체를 생성하여 BindingResult에 추가한다.


생성자

public FieldError(String objectName, String field, String defaultMessage) {}

- objectName : @ModelAttribute 이름  //  "item"
- field : 오류가 발생한 필드 이름  //  "itemName"
- defaultMessage : 오류 기본 메시지

 

글로벌 오류 (복합 룰 검증)

bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다.
    현재 값 = " + resultPrice));

 

생성자

public ObjectError(String objectName, String defaultMessage) {}

- objectName : @ModelAttribute의 이름
- defaultMessage : 오류 기본 메시지

 

 

STEP 2.  Form 수정

 

<p class="field-error" th:each="err : ${#fields.globalErrors()}"
    th:text="${err}">글로벌 오류 메시지</p>

- #fields : #fields로 BindingResult가 제공하는 검증 오류에 접근할 수 있다.

 

<input type="text" id="itemName" th:field="*{itemName}"
    th:errorclass="field-error" class="form-control" placeholder="이름을 입력하세요">
<div class="field-error" th:errors="*{itemName}">
    상품명 오류
</div>

- th:errors : 해당 필드에 오류가 있을 경우(BindingResult에 추가된), 해당 태그를 출력한다.
- th:errorclass : th:field에서 지정한 필드에 오류가 있다면 해당 class의 정보를 추가한다.

 

 

FiedlError와 ObjectError의 추가기능

사용자 입력값이 오류일 때, 그 값이 화면에 남게 하려면 어떻게 해야할까?

 

- FieldError의 생성자

  public FieldError(String objectName, String field, @Nullable Object
  rejectedValue, boolean bindingFailure, @Nullable String[] codes, @Nullable
  Object[] arguments, @Nullable String defaultMessage)

FieldError에는 두 가지 생성자를 제공한다.


- ObjectName : 오류가 발생한 객체 이름
- Field : 오류가 발생한 필드
- rejectedValue : 사용자가 입력한 값 ( 오류로 인해 거절된 값 )
- bindingFailure : 타입 오류와 같은 바인딩 실패인가, 검증 실패인가를 구분하는 값


- codes : 메세지 코드
- argmuents : 메시지에서 사용하는 인자
- defaultMessage : 기본 오류 메세지

ObjectError에서는 따로 전달받는 필드가 없으므로 Field를 따로 입력할 필요가 없다.

 

rejectedValue를 활용하여 오류 발생시 사용자 입력 값을 저장한 뒤,  타임리프의 th:field에 오류가 발생하게 되면 
FieldError에서 보관한 rejectedValue의 값을 출력한다.

 

 

rejectValue()

 

컨트롤러에서 BindingResult는 검증해야 할 객체 바로 다음에 오기때문에,  검증해야 할 객체의 세부정보를 알고있다.

@ModelAttribute Item item, BindingResult bindingResult,
                        RedirectAttributes redirectAttributes

 

- rejectValue의 생성자

void rejectValue(@Nullable String field, String errorCode,
    @Nullable Object[] errorArgs, @Nullable String defaultMessage);

-  field : 오류 필드명
-  errorCode : 오류 코드(MessageCodesResolver를 위한 오류 코드이다.)
- errorArgs : 오류 메시지에서 {0} 을 치환하기 위한 값
- defaultMessage : 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지

 

BindingResult가 제공하는 rejevtValue()를 사용하면 FieldError, ObjectError를 직접 생성하지 않고 더 간단하게 검증 오류를 다룰 수 있다.
BindingReulst는 어떤 객체를 대상으로 검증하는지 알고있기 때문에, 객체에 대한 정보는 따로 필요없으므로 제외한다.

bindingResult.addError(new FieldError("item", "price", item.getPrice(), false, 
    new String[]{"range.item.price"}, new Object[]{1000, 1000000}, null));

→ bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);

 

 

 

MessageCodesResolver

검증 오류 코드로서 메시지 코드들을 생성한다. 주로 ObjectError, FieldError와 함께 사용된다.

 

- 동작방식

1. BindingResults의 rejectValue()는 내부에서 MessageCodesResolver를 사용하여 메시지 코드들을 생성한 순서대로 보관한다.

<Field Error>

1. code + "." + object name + "." + field
2. code + "." + field
3. code + "." + field type
4. code

예) 오류 코드: typeMismatch, object name "user", field "age", field type: int 

1. "typeMismatch.user.age"
2. "typeMismatch.age"
3. "typeMismatch.int"
4. "typeMismatch"

 

2. FieldError, ObjectError의 ErrorCode를 통해 오류 코드를 자동으로 생성한다.

'MVC with 김영한' 카테고리의 다른 글

Bean Validation  (0) 2021.12.14
타임리프(thymeleaf)  (0) 2021.11.15
요청 매핑 핸들러 어뎁터  (0) 2021.10.22
HTTP 요청과 응답  (0) 2021.10.20
로깅(logging)  (0) 2021.10.17