회원 가입 API에서 DTO를 통해 사용자 입력을 받고 그 데이터가 유효한지 검증하는 코드를 아래처럼 작성했습니다.

각 필드에 대한 유효성 검사를 처리하는 로직이 필요했는데, 처음에는 컨트롤러 로직에 작성을 해서 아래와 같은 코드가 되었습니다.

위 컨트롤러 메소드 코드에서는 BindingResult 인터페이스 객체와 @Valid 어노테이션을 함께 사용하고 있는데요. 이 두 가지의 개념을 간단히 정리하면 아래와 같습니다.
@Valid: 유효성 검사를 수행할 객체에 붙이는 어노테이션입니다. 컨트롤러 메소드에서 유효성 검사를 트리거합니다. 대상이 되는 객체 안에 정의한 유효성 검사가 실행됩니다.BindingResult: 유효성 검사 결과를 담는 객체입니다. 결과에 오류가 포함되어도 그 내용을 포함해서 반환합니다.현재 코드는 컨트롤러의 로직이 너무 길다는 것인데요. 객체 지향 원칙에서 단일 책임 원칙을 고려한다면, 컨트롤러에 유효성 검증 로직까지 포함하는 것보다는 별도의 핸들러에서 처리하는 것이 나은 선택지가 될 수 있습니다. ExceptionController로 이 코드를 이동 처리했습니다.
스프링 공식 문서의 설명을 확인해 보니 BindingResult로 검증 에러를 직접 처리하지 않으면 검증이 실패했을 때 MethodArgumentNotValidException이 발생합니다.

이전에 작성한 컨트롤러에서는 BindingResult를 뒤에 이어서 에러를 수집했는데, 검증 실패시 던져지는 예외를 @ControllerAdvice 를 붙인 전역 예외 처리 클래스로 이동해서 아래처럼 처리했습니다.

제가 만든 DTO에 유효성 검사가 여러 개 있기 때문에, 각 매개변수에 대해 독립적으로 검증된 결과를 수집해야 했습니다. 실패 시 예외가 여러 개 출력되는 상황을 고려해, BindingResult로 수집한 예외들을 스트림 메소드로 받은 후 여러 예외 메시지를 콤마로 구분하도록 지정했습니다.

결과적으로 컨트롤러 로직을 깔끔하게 정리할 수 있었고, 유효성 검증 예외 처리를 중앙에서 관리할 수 있어서 더 효율적인 코드를 작성할 수 있었습니다.
참고 링크
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html
https://velog.io/@dangddoong/Spring-Valid-Exception-Handling-MethodArgumentNotValidException