FieldErrors
, ObjectError
의 생성자는 errorCode
, arguments
를 제공한다. 이것은 오류 발생시 오류 코드로 메시지를 찾기 위해 사용된다.
이전에 작성했던 messages.properties
에 추가해도 되긴 하는데 구분을 위해서 파일을 추가하자.
먼저, application.properties
파일에 아래 내용을 써야한다.
spring.messages.basename=messages,errors
설정 파일을 두개 쓰는 것이다.
파라미터 값을 넣기 위해 new Object[]
를 사용한 것을 확인할 수 있다. 어우 복잡..
참고로 파라미터 목록중에 defaultMessage
를 설정해두면, 만약에 오류 메시지에 해당하는 메시지를 못찾을때 defaultMessage
를 출력하게 된다.
또, errors.properties
또한, 국제화할 수 있다.
잘 실행되는 것을 확인할 수 있다.
FieldError
, ObjectError
는 다루기가 너무 번거롭다.
여기서 BindingResult
를 이용해서 할 수 있는데,
rejectValue()
, reject()
를 사용하면 FieldError
, ObjectError
를 직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다.
엄청 간단해졌다.
rejectValue()
field
: 오류 필드명
errorCode
: 오류 코드(이 오류 코드는 메시지에 등록된 코드가 아니다. 뒤에서 설명할 messageResolver
를 위한 오류 코드이다.)
errorArgs
: 오류 메시지에서 {0}
을 치환하기 위한 값
defaultMessage
: 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지
대충 rejectValue()
가 위에 복잡한걸 많이 줄여준다고 생각하면 된다.
오류 코드를 만들때 위 사진과 같이 자세히 만들 수도 있고
간단히 만들 수도 있다.
단순하게 만들면, 범용성이 좋아서 여러곳에서 사용할 수 있지만, 메시지를 세밀하게 작성하기 어렵다. 반대로 너무 자세하게 만들면 범용성이 떨어진다.
스프링에서는 우선순위라는게 있는데 이걸 사용하면
단순하게 한 메세지와 자세하게 만든 메세지 두 가지를 섞어서 사용할 수 잇다.
위와 같이
required
메세지와
required.item.itemName
메시지 두 개가 있다면,
위 코드에서 더 자세히 적혀있는 required.item.itemName
로 접근하게 된다.
만약 required.item.itemName
이 없다면 required
로 접근하게 된다.
이와 같이 우선순위를 설정할 수 있다.
이번 시리즈는 MessageCodesResolver에 대해 알아보는 시간이다.
위와 같이 item
오브젝트의 required
에러코드를 출력하면
순서대로 자세한게 먼저 나오고 그 뒤에 단순한게 나온 것을 확인할 수 있다.
이처럼 스프링은 메세지 코드를 자동으로 우선순위 설정을 해준다.
이번에는 더 자세히 item.itemName
필드를 검사할때 에러 코드들인데,
이 또한,
복잡 -> 단순 으로 우선순위가 정해진 것을 확인할 수 있다.
bindingResult.refectValue()
는 이 MessageCodesResolver
를 사용하기 때문에 이런 우선순위를 사용할 수 있다.
객체 오류의 경우 다음 순서로 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"
물론 모든 오류 코드를 일일이 다 작성할 수도 있다. 하지만, 이를 다 일일이 정의하면 개발자 입장에서 관리하기가 힘들다.
그렇기 때문에 크게 중요하지 않는 것들은 공통으로 관리를 하고 꼭 필요한 코드는 자세히 작성하는 방식을 사용한다.
#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
를 이용해서 더 간단하게 나타낼 수도 있다.
위의 두 코드는 같은 내용이다.
근데 저거밖에 못한다. 공백 체크만 가능.
검증 오류 코드는 2가지로 나눌 수 있다.
rejectValue()
를 직접 호출이중에서 두번째 스프링에 관해 알아볼 예정이다.
위와 같이 타입이 맞지 않을때 스프링이 자동으로 생성해주는데,
이거다.
typeMismatch.item.price
typeMismatch.price
typeMismatch.java.lang.Integer
typeMismatch
이렇게 타입이 안맞을때 메세지 설정할 수 있다.
짠
메세지 코드 생성 전략은 그냥 만들어진 것이 아니다. 조금 뒤에서 Bean Validation
을 학습하면 그 진가를 더 확인할 수 있다.