Validation - 오류 코드와 메시지 처리 시리즈

slee2·2022년 2월 5일
0

시리즈 1

FieldErrors, ObjectError의 생성자는 errorCode, arguments를 제공한다. 이것은 오류 발생시 오류 코드로 메시지를 찾기 위해 사용된다.

이전에 작성했던 messages.properties에 추가해도 되긴 하는데 구분을 위해서 파일을 추가하자.

먼저, application.properties 파일에 아래 내용을 써야한다.
spring.messages.basename=messages,errors

설정 파일을 두개 쓰는 것이다.

errors.properties

컨트롤러 - V3

파라미터 값을 넣기 위해 new Object[]를 사용한 것을 확인할 수 있다. 어우 복잡..

참고로 파라미터 목록중에 defaultMessage를 설정해두면, 만약에 오류 메시지에 해당하는 메시지를 못찾을때 defaultMessage를 출력하게 된다.

또, errors.properties 또한, 국제화할 수 있다.

잘 실행되는 것을 확인할 수 있다.

시리즈 2

FieldError , ObjectError 는 다루기가 너무 번거롭다.
여기서 BindingResult를 이용해서 할 수 있는데,

rejectValue() , reject() 를 사용하면 FieldError , ObjectError 를 직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다.

컨트롤러 - V4

엄청 간단해졌다.

rejectValue()

field : 오류 필드명
errorCode : 오류 코드(이 오류 코드는 메시지에 등록된 코드가 아니다. 뒤에서 설명할 messageResolver를 위한 오류 코드이다.)
errorArgs : 오류 메시지에서 {0} 을 치환하기 위한 값
defaultMessage : 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지

대충 rejectValue()가 위에 복잡한걸 많이 줄여준다고 생각하면 된다.

시리즈 3

오류 코드를 만들때 위 사진과 같이 자세히 만들 수도 있고
간단히 만들 수도 있다.

단순하게 만들면, 범용성이 좋아서 여러곳에서 사용할 수 있지만, 메시지를 세밀하게 작성하기 어렵다. 반대로 너무 자세하게 만들면 범용성이 떨어진다.

스프링에서는 우선순위라는게 있는데 이걸 사용하면
단순하게 한 메세지와 자세하게 만든 메세지 두 가지를 섞어서 사용할 수 잇다.

위와 같이
required 메세지와
required.item.itemName 메시지 두 개가 있다면,

위 코드에서 더 자세히 적혀있는 required.item.itemName 로 접근하게 된다.
만약 required.item.itemName이 없다면 required로 접근하게 된다.

이와 같이 우선순위를 설정할 수 있다.

시리즈 4

이번 시리즈는 MessageCodesResolver에 대해 알아보는 시간이다.

위와 같이 item 오브젝트의 required 에러코드를 출력하면
순서대로 자세한게 먼저 나오고 그 뒤에 단순한게 나온 것을 확인할 수 있다.

이처럼 스프링은 메세지 코드를 자동으로 우선순위 설정을 해준다.

이번에는 더 자세히 item.itemName 필드를 검사할때 에러 코드들인데,
이 또한,
복잡 -> 단순 으로 우선순위가 정해진 것을 확인할 수 있다.

bindingResult.refectValue()는 이 MessageCodesResolver를 사용하기 때문에 이런 우선순위를 사용할 수 있다.

DefaultMessageCodesResolver의 기본 메시지 생성 규칙

객체 오류

객체 오류의 경우 다음 순서로 2가지 생성

1.: code + "." + object name
2.: code

예) 오류 코드: required, object name: item
1.: required.item
2.: required

필드 오류

필드 오류의 경우 다음 순서로4가지 메시지 코드 생성

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"

시리즈 5

구체적인 것에서 덜 구체적인 것으로 작성

물론 모든 오류 코드를 일일이 다 작성할 수도 있다. 하지만, 이를 다 일일이 정의하면 개발자 입장에서 관리하기가 힘들다.

그렇기 때문에 크게 중요하지 않는 것들은 공통으로 관리를 하고 꼭 필요한 코드는 자세히 작성하는 방식을 사용한다.

#required.item.itemName=상품 이름은 필수입니다.
#range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
#max.item.quantity=수량은 최대 {0} 까지 허용합니다.
#totalPriceMin=가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}

#==ObjectError==
#Level1
totalPriceMin.item=상품의 가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}

#Level2 - 생략
#totalPriceMin=전체 가격은 {0}원 이상이어야 합니다. 현재 값 = {1}


#==FieldError==
#Level1
required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.

#Level2 - 생략

#Level3
required.java.lang.String = 필수 문자입니다.
required.java.lang.Integer = 필수 숫자입니다.
min.java.lang.String = {0} 이상의 문자를 입력해주세요.
min.java.lang.Integer = {0} 이상의 숫자를 입력해주세요.
range.java.lang.String = {0} ~ {1} 까지의 문자를 입력해주세요.
range.java.lang.Integer = {0} ~ {1} 까지의 숫자를 입력해주세요.
max.java.lang.String = {0} 까지의 숫자를 허용합니다.
max.java.lang.Integer = {0} 까지의 숫자를 허용합니다.

#Level4
required = 필수 값 입니다.
min= {0} 이상이어야 합니다.
range= {0} ~ {1} 범위를 허용합니다.
max= {0} 까지 허용합니다.

#추가
typeMismatch.java.lang.Integer=숫자를 입력해주세요.
typeMismatch=타입 오류입니다.

#Bean Validation 추가

NotBlank.item.itemName=상품 이름을 적어주세요.

#NotBlank={0} 공백X
Range={0}, {2} ~ {1} 허용
Max={0}, 최대 {1}

레벨 1이 없으면 레벨 2가 매칭, 2가 없으면 3이 매칭되는 방식이다.

여기서 만약 레벨 1을 주석처리하면

이렇게 다음 레벨로 넘어가는 것을 확인할 수 있다.

ValidationUtils

ValidationUtils를 이용해서 더 간단하게 나타낼 수도 있다.
위의 두 코드는 같은 내용이다.
근데 저거밖에 못한다. 공백 체크만 가능.

시리즈 6

검증 오류 코드는 2가지로 나눌 수 있다.

  • 개발자가 직접 설정한 코드 -> rejectValue() 를 직접 호출
  • 스프링이 직접 검증 오류에 추가한 경우(주로 타입 정보가 맞지 않음)

이중에서 두번째 스프링에 관해 알아볼 예정이다.

위와 같이 타입이 맞지 않을때 스프링이 자동으로 생성해주는데,

이거다.
typeMismatch.item.price
typeMismatch.price
typeMismatch.java.lang.Integer
typeMismatch

이렇게 타입이 안맞을때 메세지 설정할 수 있다.

메세지 코드 생성 전략은 그냥 만들어진 것이 아니다. 조금 뒤에서 Bean Validation을 학습하면 그 진가를 더 확인할 수 있다.

0개의 댓글