컨트롤러의 중요한 역할 중 하나는 HTTP 요청이 정상인지 검증하는 것이다. 정상 로직보다 검증로직을 잘 개발하는 것이 더 어려울 수 있다. 그만큼 검증 로직에 대해 더욱 신경써서 잘 작성해야 한다고 생각한다.
클라이언트 검증 vs 서버 검증
- 클라이언트 검증은 조작이 가능하기 때문에 보안에 취약함
- 서버만으로 검증하면 즉각적인 고객 사용성이 부족해짐
- 둘을 적절히 섞어서 사용하되, 최종적으로 서버 검증은 필수다.
- API 방식을 사용하면 API 스펙을 잘 정의해서 검증 오류를 API 응답 결과에 잘 남겨주어야 한다.
BindingResult 알아보기
- BindingResult 파라미터의 위치는 @ModelAttribute 파라미터 다음에 와야한다.
public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult)
- BindingResult가 있으면 ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다.
- ModelAttribute에 바인딩 시 타입 오류가 발생한다면?
- BindingResult 없을경우 - 400 상태코드와 함께 컨트롤러가 호출되지 않고 오류페이지로 이동
- BindingResult 있을경우 - 오류정보(FieldError)를 BindingResult에 담아서 컨트롤러를 정상 호출한다.
- BindResult는 Model에 자동으로 포함된다.
- BindingResult에 검증 오류를 적용하는 3가지 방법
- ModelAttribute의 객체에 타입 오류 등으로 바인딩이 실패하는 경우 스프링이 FieldError를 생성해서 BindingResult에 넣어준다.
- 개발자가 직접 넣어준다.
- Validator 사용
BindingResult와 Errors
org.springframework.validation.Errors
org.springframework.validation.BindingResult
- BindingResult는 인터페이스고, Error 인터페이스를 상속받고 있다.
- 실제 넘어오는 구현체는 BeanPropertyBindingResult라는 것인데, 둘다 구현하고 있으므로 BindingResult 대신에 Error를 사용해도 된다.
- Errors 인터페이스는 단순한 오류 저장과 조회 기능을 제공한다.
- BindingResult는 추가적인 기능을 더 제공해주고 있다.
- addError()도 BindResult가 제공하고 있기 때문에 웬만해선 BindingResult를 사용해주자.
FieldError / 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)
- FieldError 파라미터 목록
- objectName : 오류가 발생한 객체 이름
- field : 오류 필드
- rejectedValue : 사용자가 입력한 값(거절된 값)
- bindingFailure : 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값
- codes : 메시지 코드
- arguments : 메시지에서 사용하는 인자
- defaultMessage : 기본 오류 메시지
- ObjectError도 유사하게 두가지 생성자를 제공한다.