2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

bindingresult,rejectValue의 이해와 원리

2023. 5. 5. 01:19ㆍ[자바]스프링

오류 코드


 

우리는 코드를 짜면서 수 많은 오류코드를 검증한다.

코드를 짜는데 70퍼 이상은 검증과 오류라고 해도 과언이 아닐정도로 오류를 검증하는 일은 정말 중요한 일이다. 오류코드를 검증하는 방법은 여러가지가 있는데 이번에 가장 많이 사용되는 방법은 어떤 것이고 원리를 이해 해보자.

 

 

 

 

BindingResult란?

 

 

 

 

1.BindingResult는 스프링이 제공하는 객체 중 하나인데 객체에서 오류가 발생 시 코드를 담아주는 역활을 하는 클래스이다.

말만으로 들어서는 이해가 되지 않는다 한번 코드로 예시를 보자.

 

 

 

 

필자는 상품관리 시스템을 만들기위한 메서드이다. @ModelAttribute는 무슨뜻인지 다 안다는 가정하에

설명하겠다.

 BindingResult는 자바안에서 오류가 난다면 이 오류코드를 담는 역활을 한다.

즉 BindingResult가 있으면 자바는 타겟이 된 객체에 모든 오류 BindingResult에 담아서 저장한다.

그렇기에 개발자는 오류코드가 어떤 오류 코드가 터졋는지 전부 알 수 있다는 뜻이 된다.

하지만 이 오류코드를 확인 할 수 있는 방법은 어떤게 있을까? 코드로 한번보자.

 

 

 

 

나는 이 BindingResult를 선언한다면 타겟이 된 객체에 있는 모든 오류를 저장한다.

 

이 표시한 코드를 본다면 bindingresult.hasError라고 선언했다. 이것은 오류코드가 있다면 

오류코드를 출력하고 다시 form객체로 전송하는 것이다. 즉 우리가 웹사이트를 사용할때

대부분 사용자가 잘못입력한 부분이나 (회원 가입) 같은 경우 오류코드가 발생한다면 다시 그 회원 폼으로 되돌리는 로직이다.

 

즉 BindingResult는 타겟이 된 객체에 있는 모든 오류들을 담아서 저장하는 역활을 한다.

 

 

BindingResult의 메서드의 호출

 

 

BindingReult의 메서드는 여러가지 메서드를 지원하는데 자주 사용하는 것만 본다면 이렇게 있을 것 이다.

 

 

getFieldError();

 

-> 필드 에러를 호출한다.

 

getTarget()

- TagetError를 호출한다. TargetError라는것은 밑에서 설명할 바인딩할 객체의 정보(타겟)을 호출한다.

 

getAllErrors()

 

- 타겟이 된 객체의 모든 오류를 출력한다.

 

 

BindingResult의 사용방법은?

 

 

일단은 나의 ItemDto는 간단하게 이러한 필드를 가지고있다. 

 

 

이 BindingResult의 사용방법은 반드시 알아야 하는데 그냥 아무렇게나 사용할 수 있는 것이 아니다.

 

1.Target이 되는 객체 옆에 선언해야한다.

- 즉 이게 무슨뜻이냐한다면 ModelAtrribute로 담은 객체 바로옆에 선언해줘야 한다.

 

 

 

 

즉 이런식으로 BindingResult를  반드시 객체옆에 선언해줘야 하는 것이다.

 

그 이유는 BindingResult는 Target이 되는 객체에 모든 오류코드를 담고 있기 때문에 

이 ItemDto 객체에 관련해서 오류가 날 경우 BindingResult에 담기 때문이다.

 

그렇기 때문에 BindingResult는 Dto에 나는 모든 에러를 담을 수 있게 된 것 이다.

 

그렇기에 BingingResult.getTaget()이란 메서드를 찍어본다면 객체의 정보가 출력되는

것을 확인 할 수 있다.

 

 

2. BindingResult를 쓰기 위해선 @ModelAtrribute를 써줘야 한다.

안쓰면 객체 바인딩을 못한다

 

3.만약 템플릿엔진에 오류를 담고싶다면 @ModelAttribute에 모델 이름이랑 같아야 한다.

 

 

 

BindingResult의 에러추가

BindingResult의 작동원리는 간단하지만 어렵다.

일단은 BindingResult를 선언하고

BindingResult에 있는 메서드에 에러를 추가하면 된다.

에러를 추가하는 방법은 여러가지가 있는데

 

 

  public static BindingResult ValidationV2(BindingResult result,ItemDto dto){
        if(!(StringUtils.hasText(dto.getItemName()))){
          result.addError(new FieldError("ItemDto","itemName","상품 이름은 필수"));
      }
        if(dto.getPrice() == null || dto.getPrice() < 1000 || dto.getPrice() > 10000000){
            result.addError(new FieldError("ItemDto","price","" +
                    "가격은 1,000 ~ 1,000,000까지 입니다"));
        }
        if(dto.getQuantity() == null || dto.getQuantity() > 9999){
            result.addError(new FieldError("ItemDto","quantity","최대 값은 9999 입니다."));
        }

 

 

 

 

사용방법은 단순하다.

IF로 조건을 걸어준다음 그 조건이 TRUE라면

에러 메세지를 넣으면 되는 것 이다.

 

 

 

addError 메서드로 에러코드를 넣는다.

addError를 사용할 경우 반드시 new FieldError를 객체를 만들어야 한다.

 

new FieldError에 들어가는 파라미터들은

 

 [오브젝트 명 = 객체명 ]  [필드 에러]  [에러 메세지]

 

오류 코드를 넣기위해선 BindingResult는 반드시 알아야 하는 것이 있다.

 

오브젝트명과 필드 에러 부분이다. 

 

어떤 객체에서 오류가 난건지 BindingResult는 알아야만 하고.

어떤 필드에서 오류가 난 것인지 알아야 한다.

오류메세지는 내가 선언한 메세지가 없다면 자바 스프링이 가지고 있는 디폴트 메세지를 출력하게 된다.

 

 

하지만 addError를 쓴다면 [오브젝트명]  [필드에러]  [에러 메세지] 세 가지를 정의 해줘야 한다.

 

앞서 말했지만 BindingResult는 이미 객체명을 알고 있다.

왜냐하면 BindingResult를 사용하기 위해선 반드시 타겟이 되는 객체를 옆에 선언해줘야 하는데

이 부분에서 이미 BindingResul는 객체를 알고 있는 것이다.

그렇다면 더 쉬운 메서드는 없을까? 이 메서드를 알아보기전에 에러코드를

더 깊게 알아보자 .

 

 

 

사용자 정의 오류코드와 스프링 에러 코드

 

앞서 말했듯이 BindingResult는 타겟이 되는 객체의 모든 에러코드를 받는다고 했다.

저기 에러에서 나는 아이템이름이 널 이거나 아이템가격이 널이거나 조건이 부합하지 않는다면

에러코드를 출력하는 메서드를 작성했다. 하지만 아이템 가격은 Int인데 여기에 문자를 넣어보면 어떻게 될까?

Int이기때문에 문자가 들어가지 않기 때문에  TypeMisMatch가 나오게 될 것이다. 

하지만 나는 저기 메서드에서 어떠한 곳에도 타입미스매치라는 오류코드를 만든적이 없다.

한번 확인 해보자.

 

 

가격부분에 ㅂㅂㅂ라는 문자를 넣으니 스프링에서 제공하는 Default메세지가 출력되는걸 확인 할 수 있다.

이것이 스프링이 제공한 기본 에러코드이다.

 

사용자 정의 에러는 내가 정의한 에러코드가 출력되는 것이다.

 

 

 

 

 

 

 

 

 

rejectValue와 reject

 

앞서 말했듯이 Bindindresult는 이미 객체명을 알고있다고 했고 오류코드에 들어갈 조건으로 반드시

객체명과 필드가 필요하다고 했다.

그렇다면 이 더 간단하게 이 메서드를 사용하면 된다.

 

 

 

 

앞서 본 코드와 별반 다르지 않은 코드이다.

이 rejectValue는 3가지 조건이 들어가게 된다.

 

 [필드명]    [에러 코드]    [메세지 내용]

 

여기서 모순이 발생하게 된다. 오류코드를 만들려면 반드시 객체명과 필드명이 있어야 한다하는데

객체명이 없다. 그렇다면 이 오류메서드는 잘못된 것 일까?

 

아니다 앞서 말했듯이 BindingResult는 ModelAttribute때문에 이미 타겟이 된 객체명을 알고 있다.

그렇기 때문에 rejectValue를 사용하면 오브젝트명은 생략이 되었지만 자동으로 들어가게 된다.

 

 

 

 

rejectValue와 reject의 사용방법 

 

rejectValue를 사용하기 위해서는 알아야 할 것이 몇가지 있다.

 

1.반드시 오브젝트명에 있는 필드명을 사용할 것.

 

예를들어 나는 price에 값이 널이거나 조건이 부합하다면 오류코드를 출력하고싶다. 그렇기에 

이 price필드를 넣는 것 이다.

몇번이나 말했지만 Bindingresult는 타겟이 되는 객체를 오류코드를

만들어 준다고 했다. 그러니까 당연히 .. 바인딩이 된 ItemDto안에 있는 필드를 써줘야 한다.

이건 당연한거지만 .. BindingResult를 사용할때 타겟이 되는 객체에 Getter를 선언해줘야 한다.

 

이건 Bindingresult뿐만 아니라 스프링에 바인딩을 할때 스프링이 해당 필드를 가져오기 위해서는

Getter메서드로 가져오기 때문이다.

 

 

 

 

rejectValue와 reject의 작동원리

 

 

 

그렇다면 이 reject의 작동원리는 어떻게 될까? 한번 알아보도록 하자.

 

 

1.Field

=> 앞서 말했듯이 에러코드를 만들기 위해서는 반드시 필드명이 필요하다고 했다. 그렇기에

어떤 필드에 있는 에러를 사용할 것인지 IF문으로 조건을 걸고 그 필드명을 적어주면 된다.

 

2.ErrorCode

 

이 에러코드는 깊게 알아야 할 필요가 있는데 ..

 

기본적으로 이 파라미터에는 properties에 있는 메세지가 들어가게 된다.

이게 무슨뜻이냐면 BindingResult는 기본적으로 메세지리졸버라는것을 사용하게 되는데

 

우리가 에러코드를 만든다면 스프링을 이렇게 에러코드를 만들게 될 것이다. 

[에러코드].[오브젝트 명].[필드명]

즉 ErrorCode부분에 required라고 에러코드를 지정한다면

 

required.ItemDto.itemName으로 에러코드를 만든다. (총 4개를 만듬)

 

만약에 properties를 사용하게 되지 않는다면 에러코드는 아무 값이나 넣어도 동작하게 된다.

그 이유는 ErrorCode에 어떤 에러코드를 넣는다면 스프링은 properties있는 에러코드를 찾게 될 것이고

그 에러코드가 없다면 우리가 선언한 메세지 내용을 출력하게 된다.

 

reject를 사용해서 값을 넣게되면 스프링은 총 4개의 에러코드를 만들게 되는데..

 

우리가 reject를 이용해 파라미터를 넣을때 

 

[필드 명] [에러 코드]을 넣게 된다고 했다.

 

그렇다면 reject는 String 배열에 총 4개의 에러코드를 만든다.

 

 

 

예시로 이 코드를 보자

이 코드에 rejectValue에 

[filed] quantity [ErrorCode] bad_quantity [message] =

 

이렇게 파라미터 값을 넣는다면

스프링은

required.ItemDto.ItemName

requried.ItemName

requried.String

requried

 

 

 

 

 

즉 이렇게 넣어서 메세지 리졸버에 넣는다. 그 후 ErrorCode는 제일 자세한 순서대로 에러코드를 찾게 된다.

이 과정을 properties에서 찾게 되는데 이 4개의 에러코드를 찾지 못한다면 우리가 선언한 메세지 내용을 출력 하게 되는 것 이다.

 

 

하지만 여기서 의문이 든다. 만약 글로벌 에러라면 필드가 존재하지 않는데 어떻게 해결해야할까?

 

예를들어 우리는 가격 * 수량이 총 2만원이상이 넘어야만 주문을 하게 하고싶다.

하지만 가격 * 수량필드를 만드는것도 코드낭비 같고 이 가격 * 수량은 필드에 존재하지 않는다.

그럴때 사용하는 것이 reject이다.

 

 

 

이렇게 RejectValue처럼 사용해주면 된다.

 

 

이렇게 검증 오류코드에 대해 알아봤다. 이정도만 알아도 검증오류코드를 쓰는데에는 충분하지 않을까 싶다.

 

만약 이 오류코드로 동적 템플릿엔진 = 타임리프 같은것을 사용할때에는 반드시동적 파라미터가 들어가는 부분에는 객체 타겟이 되는 객체와 이름을 같게 해줘야 한다.Bindingresult가 자동으로 modelattribute에 넣어주지만 모델에 들어갈때에는 객체명으로 들어가기 때문에

 

th:errors를 작성할때에는 ItemDto.itemName등으로 사용해줘야 한다. 아니면 템플릿엔진이 에러코드를 못찾는다.