(1) 검증

CJY·2023년 3월 27일
0

스프링

목록 보기
6/14

컨트롤러의 중요한 역할 중 하나는 HTTP 요청이 정상인지 검증하는 것이다.

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

클라이언트 검증은 HTTP를 POSTMAN과 같은 프로그램으로 조작할 수 있다. 그렇다고 서버만으로 검증하면 고객의 즉각적 사용성이 부족해진다. 따라서 두 개를 섞어서 사용하되 서버의 검증은 최종적으로 필수이다.
API 방식을 사용하면 스펙을 잘 정의해서 검증 오류를 API 응답 결과에 포함시켜주어야 한다.

1단계

Java 언어로 검증하기

검증 오류를 보관할 map 생성

Map<String, String> errors = new HashMap<>();

빈 값 입력

!StringUtils.hasText(상품.get이름())

다음과 같이 import org.springframework.util.StringUtils;를 import하고 이름이 비었는지 확인한다. 나머지 로직도 if문을 통해 검증 오류가 있다면 errorsput을 한다.

검증에 실패시

if (!errors.isEmpty()) {
 	model.addAttribute("errors", errors);
 	return "입력폼주소";
}

다음과 같이 errors가 비어있지 않다면 같은 폼으로 되돌려 보낸다.

뷰 템플릿

th:classappend="${errors?.containsKey('필드명')} ? '에러클래스명' : _"
th:if="${errors?.containsKey('필드명')}" th:text="${errors['필드명']}

다음과 같이 작성하면 된다.
여기서 errors?문법은 스프링의 SpringEL이 제공하는 문법인데 null일 때 NullPointerException이 발생하는 대신 null을 반환하게 해준다.

문제점

  • 타입 오류 처리가 안된다. 만약 상품 클래스에서 필드의 타입과 맞지 않은 값을 클라이언트가 보낸다면 컨트롤러가 호출되지 않고 400예외를 보여주며 오류 페이지로 넘어간다.
  • 뷰 템플릿에서 중복된 코드가 많다.
  • 고객이 입력한 문자를 저장하고 다시 보여줄 수가 없다.

2단계

BindingResult

스프링이 제공하는 검증 오류 처리 방법이다. 컨트롤러 메서드에서 BindingResult 파라미터의 위치는 Model 바로 다음에 와야 한다.

addError

BindingResult의 함수인 addError를 통해 오류가 있는 필드나 객체를 넣어줄 수 있다.

BindingResult bindingResult를 파라미터로 받고
bindingResult.addError(new FieldError(...));
bindingResult.addError(new ObjectError(...));

FieldError

두 가지 생성자

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

파라미터 목록
objectName : 오류가 발생한 객체 이름
field : 오류 필드
rejectedValue : 사용자가 입력한 값(거절된 값)
bindingFailure : 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값
codes : 메시지 코드
arguments : 메시지에서 사용하는 인자
defaultMessage : 기본 오류 메시지

ObjectError

생성자

public ObjectError(String objectName, String defaultMessage) {}

특정 필드를 넘어서는 오류가 있으면 ObjectError 객체를 생성해서 bindingResult 에 담아두면 된다.
objectName : @ModelAttribute 의 이름
defaultMessage : 오류 기본 메시지

타임리프 스프링 검증 오류 통합 기능

타임리프는 스프링의 BindingResult를 활용해서 편리하게 검증 오류를 표현하는 기능을 제공한다.
#fieldsBindingResult가 제공하는 검증 오류에 접근할 수 있다.
th:errors: 해당 필드에 오류가 있는 경우에 태그를 출력.
th:errorclass: th:field에서 지정한 필드에 오류가 있으면 class정보를 추가한다.

문제점 해결

FieldError를 통해 잘못된 타입값이 들어와도 컨트롤러가 호출된다. 또한 해당 오류를 BindingResult에 담아서 컨트롤러를 호출하여 사용자가 입력한 오류값을 저장하고 th:field에서 그 오류값을 보여준다.
참고로 BindingResultErrors인터페이스를 상속받고 있는 인터페이스다. 관례상 BindingResult를 많이 사용한다.

rejectValue(), reject()

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

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

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

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

MessageCodesResolver

  • 검증 오류 코드로 메시지 코드들을 생성한다.
  • MessageCodesResolver 인터페이스이고 DefaultMessageCodesResolver 는 기본 구현체이다.
  • 주로 다음과 함께 사용 ObjectError , FieldError

객체 오류

객체 오류의 경우 다음 순서로 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"

Validator

기본적으로 직접 validator를 만들기 위해서는
@Component를 통해 빈 등록을 하고 implements Validator를 통해 supportsvalidate함수를 @Override해야 한다.
supports함수는 나중에 @Validated@Valid가 붙어있는 객체에 어느 validator를 사용할 지 확인하는 용도이며 validate함수는 실제로 검증을 하는 로직이 들어있는 함수다.

@Validated

해당 어노테이션은 파라미터에서 모델 객체 앞에 붙여주면 된다. 예를 들어 Item이란 객체를 파라미터로 받을 때는 @Validated @ModelAttribute Item item과 같은 식이다.

3단계

Bean Validation

해당 내용은 다음 포스터에 기재할 예정이다.

profile
열심히 성장 중인 백엔드

0개의 댓글