Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

검증 (validation) 본문

Spring/Spring MVC - 웹 개발 활용 기술

검증 (validation)

studyHub 2024. 7. 16. 23:56

고객이 입력한 값을 검증할 필요가 있다.

클라이언트 검증은 조작할 수 있으므로 보안에 취약하다 (postman만 써봐도 우회가 가능)

서버만으로 검증하면, 즉각적인 고객 사용성이 부족해진다.

=> 둘을 적절히 섞어서 사용하되, 최종적으로 서버 검증은 필수

 

먼저 검증을 직접 구현해보고, 뒤에서 스프링과 타임리프가 제공하는 검증 기능을 활용해보자


 

version1. 순수 검증 직접 구현

실패한 경우 고객에게 다시 상품 등록 폼을 보여주고, 어떤 값을 잘못 입력했는 지 알려줘야 한다.

 

검증 로직

 

에러 메시지를 모델에 담아 뷰로 보내고, 뷰에서 처리한다.

 

화면 처리

errors?. => NPE 대신 null 반환하고 이후 진행x. (->최초 빈객체 보낼 때 처리 가능)

 

화면 처리 시 고객이 이전에 입력한 데이터가 보이는 이유는, 애초에 객체를 보내도록 설계했기 때문이다.

 

 

문제는 중복이 너무 많고, 번거롭다는 것과

타입 검증이 불가하다는 것이다. 타입 오류는 컨트롤러에 오기 전에 바인딩 오류가 발생해서 400에러가 발생한다.

 

 


 

version2. 스프링이 제공하는 검증 방법

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

BindingResult는 스프링이 제공하는 검증 오류를 보관하는 객체이다. 검증 오류가 발생하면 여기에 보관하면 된다.

BindingResult가 있으면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다!

 

@ModelAttribute에 바인딩 시 타입 오류가 발생하면

  • BindingResult가 없으면 400 오류가 발생하면서 컨트롤러가 호출되지 않고, 오류 페이지로 이동
  • BindingResult 가 있으면 오류 정보( FieldError )를 BindingResult 에 담아서 컨트롤러를 정상 호출

 

BindingResult에 검증 오류를 적용하는 3가지 방법

  • @ModelAttribute의 객체에 타입 오류 등으로 바인딩 실패 시 스프링이 FieldError 생성해서 BindingResult에 넣어줌
  • 개발자가 직접 넣어준다.
  • Validator 사용 (뒤에서 설명)

BindingResult 는 검증할 대상 바로 다음에 와야한다. 순서가 중요하다. BindingResult 는 Model에 자동으로 포함된다

 

타임리프는 스프링의 BindingResult 를 활용해서 편리하게 검증 오류를 표현하는 기능을 제공한다.

  • #fields : #fields 로 BindingResult 가 제공하는 검증 오류에 접근할 수 있다.
  • th:errors : 해당 필드에 오류가 있는 경우에 태그를 출력한다. th:if 의 편의 버전이다.
  • th:errorclass : th:field 에서 지정한 필드에 오류가 있으면 class 정보를 추가한다.

 

 

 

문제 상황 : 오류가 발생하는 경우 고객이 입력한 내용이 모두 사라진다. 이 문제를 해결해보자

수량이 9999를 넘긴 경우, 응답 화면에 이전 입력 값이 사라진다. 한편 가격의 바인딩 오류의 값은 남았다?

 

 

FieldError, ObjectError에서 오류 발생시 사용자 입력 값 유지

FieldError는 두가지 생성자를 제공하는데, rejectedValue 파라미터를 입력하지 않을 경우 이전 입력 값이 보존되지 않는다.

rejectedValue가 바로 오류 발생시 사용자 입력 값을 저장하는 필드

rejectedValue를 입력한 생성자로 FiledError를 생성하여 이전 입력 값 보관

 

스프링의 바인딩 오류 처리

타입 오류로 바인딩에 실패하면 스프링은 FieldError를 생성하면서 사용자가 입력한 값을 넣어둔다.
그리고 해당 오류를 BindingResult 에 담아서 컨트롤러를 호출한다.
따라서 타입 오류 같은 바인딩 실패시에도 사용자의 오류 메시지를 정상 출력할 수 있다

 

타임리프의 사용자 입력 값 유지

타임리프의 th:field는 매우 똑똑하게 동작하는데,

정상 상황에는 모델 객체의 값을 사용하지만, 오류가 발생하면 FieldError 에서 보관한 값을 사용해서 값을 출력한다.

 

 


오류 코드와 메시지 처리

 

MessageSource를 이용해서 메시지를 일관적으로 관리

FieldError, ObjectError 생성자는 codes, arguments를 제공하여  오류 발생시 오류 코드로 메시지를 찾기 위해 사용한다.

errors.properties 라는 별도의 파일로 관리하고, 스프링 부트가 해당 메시지 파일을 인식할 수 있게 다음 설정을 추가한다.

application.properties에 spring.messages.basename=messages,errors 추가

 

너무 길고 번거롭다. 축약해보자.

 

컨트롤러에서 BindingResult 는 검증해야 할 객체인 target 바로 다음에 온다.
따라서 BindingResult 는 이미 본인이 검증해야 할 객체인 target 을 알고 있다. ( bindingResult.getTarget() ) 

 

BindingResult 가 제공하는 rejectValue() , reject() 를 사용하면 FieldError , ObjectError 를 직접 생성하지 않고,

깔끔하게 검증 오류를 다룰 수 있다.

MessageCodesResolver가 target.filed.code로 오류메시지를 찾아준다.

 메시지 설계 tip : 레벨 전략. 범용성있게 사용하다가 -> 필요시 디테일 추가

rejectValue() , reject() 는 내부에서 MessageCodesResolver 를 사용한다. 여기에서 메시지 코드들을 생성한다.

MessageCodesResolver 는 오류 메시지에 required.item.itemName 와 같이 객체명과 필드명을 조합한 세밀한 메시지 코드가 있으면 이 메시지를 높은 우선순위로 사용한다.

 

 

정리

  1. rejectValue() 호출
  2. MessageCodesResolver 를 사용해서 검증 오류 코드로 메시지 코드들을 생성
  3. new FieldError() 를 생성하면서 메시지 코드들을 보관
  4. th:erros 에서 메시지 코드들로 메시지를 순서대로 메시지에서 찾고, 노출

 

스프링이 직접 만든 오류 메시지 처리

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

  • 개발자가 직접 설정한 오류 코드 rejectValue() 를 직접 호출
  • 스프링이 직접 검증 오류에 추가한 경우(주로 타입 정보가 맞지 않음)
    스프링은 타입 오류가 발생하면 typeMismatch 라는 오류 코드를 사용한다

 

Validator 분리

목표 : 컨트롤러의 복잡한 검증 로직을 별도로 분리하자

 

validator 클래스를 만들고 검증 로직을 넣는다.

 

validator를 빈으로 주입 받고, WebDataBinder에 매번 초기화

 

 

컨트롤러에서 validator 직접 호출하는 방식

 

Validator 인터페이스를 상속받는 이유

검증기 호출을 자동화 해준다. ( support()로 해당 검증기를 찾는다.)

 

WebDataBinder 는 스프링의 파라미터 바인딩의 역할을 해주고 검증 기능도 내부에 포함한다.

WebDataBinder 에 검증기를 추가하면 해당 컨트롤러에서는 검증기를 자동으로 적용할 수 있다.

validator를 직접 호출하는 부분을 지우고, 대신에 검증 대상 앞에 @Validated를 붙이면 된다.

@Validated 는 검증기를 실행하라는 애노테이션

 

 

 


이렇게 Validator, BindingResult를 사용해서 검증 로직의 사용성을 개선할 수 있지만.. 여기서 더 줄 일 수 있다!

=> Bean Validation을 알아보자!


 

참고 자료 & 이미지 출처
스프링 MVC 2편 (김영한 님)


 

'Spring > Spring MVC - 웹 개발 활용 기술' 카테고리의 다른 글

예외 처리와 오류 페이지  (0) 2024.08.10
로그인 처리2 - 필터, 인터셉터  (0) 2024.08.10
로그인 처리 - 쿠키, 세션  (0) 2024.08.06
Bean Validation  (0) 2024.08.03
타임 리프  (1) 2023.12.17