Spring

[SpringBoot] Validation 간단 정리 - 1 (BindingResult, Validator)

꾸준함. 2021. 7. 16. 23:05

개요

실무 프로젝트를 경험하신 분들은 다들 공감하시겠지만 프로젝트 개발 과정에서 실제 개발에 투자하는 시간만큼 예외처리 및 validation을 진행합니다.

해당 게시글은 PRG 패턴을 예시로 들어 내용을 정리할 예정이며 PRG 패턴에 관해서는 아래 게시글을 참고해주세요.

(https://jaimemin.tistory.com/1825)

 

게시글에서 다룰 케이스는 아래와 같습니다. (위에는 PRG 패턴 정상 케이스, 아래는 게시글에서 다룰 케이스)

 

회원가입 제약 조건

우선, 검증 및 예외처리를 하기 위해서는 회원가입을 진행할 때 제약 조건이 있어야 합니다.

따라서, Member 즉 회원 클래스에는 id, password, 그리고 생년을 나타내는 year 필드가 있다고 가정합니다.

 

각 필드에 대한 제약조건은 아래와 같습니다.

1. id는 중복되어서는 안 됩니다.

2. password는 대문자, 소문자, 그리고 특수문자를 반드시 포함해야 하며 길이가 최소 8자리, 최대 20자리입니다.

3. year의 경우 1900 ~ 2021 사이 자연수여야 합니다.

4. id, password, year는 모두 필수 값입니다.

 

Validation 버전 1

제일 먼저 떠오르는 방법은 컨트롤러에서 아래 코드와 같이 직접 예외처리를 진행하는 방법입니다.

검증 시 오류가 발생할 경우 errors에 담아두는데 register 폼 내 어떤 필드에 오류가 발생했는지 파악하기 위해 실제 input 필드명과 매칭 하여 오류 메시지를 value로 저장합니다.


 

위 코드처럼, 스프링 프레임워크의 도움 없이 예외처리를 진행하는 것도 나쁘지 않은 방법이지만 몇 가지 문제점이 있습니다.

1. year 필드의 경우 Integer 타입인데 문자열이 입력됐을 경우 타입 오류 처리가 진행이 되지 않습니다. 회원가입을 진행하는 과정에서 year 필드에 qqq와 같은 문자를 입력할 경우 의도한 것처럼 회원 가입 폼으로 돌아가는 대신 400 에러 페이지가 보일 것입니다.

2. 사용자가 기존에 기입한 문구와 함께 예외처리 메시지를 봐야 어떤 부분을 잘 못 입력했는지 한 번에 파악이 가능합니다. 예를 들어, 키보드를 안 보고 1991을 year 필드에 입력을 했는데 한 칸 아래 문자들을 입력하여 qppq를 입력했을 경우 예외처리 문구만 보고서는 자기가 무엇을 잘 못 입력했는지 파악하기 힘듭니다. 따라서, 1번의 타입 오류를 해결했더라도 year 필드가 Integer 타입이고 Integer 타입은 문자열을 저장할 수 없기 때문에 사용자가 입력한 문구를 보여줄 수 없는 문제가 발생합니다.

3. 2번의 문제 때문에 고객이 입력한 값들은 별도로 관리가 되어야 합니다.

 

Validation 버전 2

이번에는 스프링에서 제공하는 BindingResult 객체를 사용하여 예외처리를 진행해보겠습니다.

BindingResult는 검증 오류가 발생할 경우 오류 내용을 보관하는 스프링 프레임워크에서 제공하는 객체입니다. (앞선 2, 3번 문제 해결)

또한, BindingResult가 있으면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 오류 정보를 FieldError 개체를 BindingResult가 담은 뒤 컨트롤러가 호출됩니다. (앞선 1번 문제 해결)

여기서 주의할 점은 BindingResult 객체의 파라미터 위치는 반드시 @ModelAttribute 어노테이션이 붙은 객체 다음에 위치해야 한다는 점입니다.

우선 코드를 작성한 뒤 보다 자세한 설명은 밑에서 하겠습니다.


 

버전 1과 달라진 점은 크게 두 가지입니다.

1. errors를 별도로 선언하는 대신 스프링 프레임워크에서 제공하는 BindingResult 객체를 사용하였습니다.

2. BindingResult는 Model에 자동으로 포함되기 때문에 Model 객체를 파라미터에 추가하지 않았습니다.

 

정리를 하자면, BindingResult에서 검증 오류를 적용하는 방법은 아래와 같습니다.

1. @ModelAttribute의 객체가 타입 오류와 같은 오류 때문에 바인딩이 실패할 경우 스프링 프레임워크에서 FieldError 객체를 생성해서 BindingResult에 넣어주는 경우

2. 개발자가 직접 넣어주는 경우 (예시에는 작성하지 않았지만 필드는 모두 정상적으로 입력되었지만 기획에 따라 필드를 조합했을 때 예외가 발생하여 개발자가 직접 넣어주는 경우도 있습니다. 이 때는, ObjectError 필드를 BindingResult에 넣어줘야 함)

3. Validator 객체 사용 (버전 3)

 

Validation 버전 3

버전 2에서 BindingResult를 적용하였지만 여전히 버전 1처럼 컨트롤러 내 검증 로직이 존재하여 코드가 지저분해진다는 단점이 존재했습니다.

따라서, 이번에는 검증 로직을 컨트롤러에서 분리할 수 있도록 Validator를 적용해봅니다.

마찬가지로 우선 코드를 작성하고 자세한 설명은 밑에서 진행하겠습니다.


 

스프링 프레임워크는 검증을 체계적으로 진행하기 위해 아래의 인터페이스를 제공합니다.

 

public interface Validator {

	boolean supports(Class<?> clazz);

	void validate(Object target, Errors errors);
}

 

supports 메서드는 Validator가 해당 클래스를 지원하는지 확인하는 메서드입니다.

validate 메서드는 검증 대상 객체와 BindingResult 즉, 검증 로직을 수행하는 메서드입니다.

 

이렇게 Validator를 별도로 분리하면 아래와 같이 컨트롤러가 훨씬 깔끔해지는 것을 확인할 수 있습니다.

물론, Validator를 적용하기 위해서는 Controller에 MemberValidator를 생성자 주입해줘야 합니다.


 

여기서 더 욕심을 낸다면 아래와 같이 WebDataBinder 객체를 사용하면 됩니다.


 

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

위 코드처럼 WebDataBinder에 Validator를 추가하면 해당 컨트롤러에서는 Validator를 자동으로 적용할 수 있으며, 앞서 설명했던 Validator의 supports 메서드를 통해 해당 Validator를 적용할지 여부를 정합니다. (MemberValidator 뿐만 아니라 다른 Validator가 존재할 수 있으므로 supports 메서드는 중요한 역할을 합니다.)

주의할 점은, WebDataBinder를 적용하기 위해서는 @ModelAttribute 어노테이션 앞에 @Validated 어노테이션을 추가해야 한다는 점입니다.

 

여기서 한 단계 더 욕심을 내서, 모든 컨트롤러에 해당 Validator가 적용되도록 글로벌하게 설정할 수 있는 방법이 있는데 컨트롤러 내부에 국한되도록 설정하는 것을 추천드립니다.

그래도 적용을 하고 싶다면 아래와 같이 적용을 하면 됩니다.


 

비고

앞서 정리한 메시지 기능(https://jaimemin.tistory.com/1870)을 통해 FieldError 혹은 ObjectError의 오류 메시지를 체계적으로 관리할 수 있습니다.

이는 스프링 프레임워크에서 제공하는 MessageCodeResolver를 통해 이루어지며 보다 자세한 내용은 아래 링크를 참고하시길 바랍니다.

https://kapentaz.github.io/spring/Spring-Boo-Bean-Validation-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90/#

 

Spring Boot Bean Validation 제대로 알고 쓰자

지난번에 작성한 Java Bean Validation 제대로 알고 쓰자에 이어서 Spring Boot 환경에서 Validation을 어떻게 사용할 수 있는지 확인해보겠습니다. Spring에서도 Hibernate Validator를 사용합니다. Java Bean Validation

kapentaz.github.io

 

출처

인프런 스프링 MVC 2편 (김영한 강사님)

 

 

 

반응형