Validation 과 Exception Handler

Legday_Dev·2023년 9월 7일
0

Spring

목록 보기
7/14
post-thumbnail

개발을 하다보면 CRUD 기반으로 로직을 짠다. 실무에서는 CRUD 기반으로 개발하는것 보다는 유효성검사나 예외처리 같은 부분에 더 많은 시간을 쓴다고 한다.

전/후 처리 개념

  • 만약 Client가 ssar 이라는 이름으로 회원가입을 요청한다고 하자. 만약 DB에 이미 ssar 이라는 이름이 있다면 서버는 클라이언트에게 오류를 알려줘야한다 -> 후처리(Exception Handler)
  • 만약 Client가 20글자 제한이 있는 이름을 20자 넘게 입력하게 된다면 서버에 요청이 가기전에 앞에서 오류를 알려줘야 한다 -> 전처리(Validation)
  • 전처리는 Validation(유효성검사) , 후처리는 Exception Handler 로 처리한다.
  • 회원가입을 할 때는 이런 전/후 처리가 필요하다
    • 회원가입같은 기능을 핵심기능이라한다.
    • 회원가입 기능을 위한 전처리, 후처리 같은 기능을 AOP(관점 지향 프로그래밍) 이라 한다.

Validatino 세팅

  • Validatino 라이브러리를 pomxml 에 추가해줘야 한다.

      <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        <version>2.4.4</version>
    </dependency>

    DB와 직접적으로 연결되는 Domain 에는 JPA 가 제공하는 Validaton 기능들을 사용해야 한다. 이 라이브러리에서 사용하는 Validation 은 DTO와 같은 곳에 사용한다 !!

Validatino 구현하기

  • 클라이언트가 회원가입 할 때 보내는 데이터들은 DTO에 담아서 통신한다.
  • 예를 들어 username 을 최소 2자에서 20자 이내로만 받고, 나머지 데이터들은 빈값없이 무조건 받아야 한다고 가정해보자.

@Data : Getter , Setter , toString() 등등 편리한 어노테이션을 한꺼번에 제공
@Size(min = 2, max = 20) : 최소 2자 에서 최대 20자까지만 받는다
@NotBlank : Null, 빈문자열, 공백만 있는 문자는 못받는다

@Data
public class SignupDto {
    @Size(min = 2,max = 20) //길이 20자 제한
    @NotBlank
    private String username;
    @NotBlank //null, 빈문자열, 공백만있는 문자불가
    private String password;
    @NotBlank
    private String email;
    @NotBlank
    private String name;
	//생략...
}

Validation 사용 예제

  • View 에서 넘어오는 데이터를 DTO 로 Controller 에서 받을 때는 DTO 객체 앞에 @Valid 를 꼭 붙여준다.
  • 바로 뒤에 BindingResult 라는 객체를 파라미터로 넣는다. (반드시 @Valid 하는 객체 뒤에 와야 한다 !!)
    • BindingResultValidation 을 했을 때 오류를 담는 객체이다.
  • BindingResult 에서 제공하는 hasError() 메서드는 에러가 있으면 true, 없으면 false을 반환
  • 에러가 있다면 에러와 에러메시지를 담을 Map 컬렉션을 선언해준다.
  • for-each 문을 돌려서 error를 Map 컬렉션에 넣어준다.
  • CustomValidationException 이라는 개발자가 만든 예외를 던져준다 !
@PostMapping("/auth/signup")
public String signUp(@Valid SignupDto signupDto, BindingResult bindingResult){
    if(bindingResult.hasErrors()){ // Validation 후 error 가 있다면
        Map<String, String> errorMap = new HashMap<>();

        for (FieldError error : bindingResult.getFieldErrors()) {
            errorMap.put(error.getField(),error.getDefaultMessage());
        }
        throw new CustomValidationException("유효성 검사 실패함",errorMap);
    }else{
        User user = signupDto.toEntity();
        User userEntity = authService.회원가입(user);
        return "auth/signin"; //로그인 페이지로 이동
    }
}

CustomValidationException -> 개발자가 만든 예외처리 로직

CustomValidationException 은 개발자가 따로 예외처리를 하기 위해 RuntimeException 을 상속받아 만든 클래스이다.

  • RuntimeException 을 상속받은 이유는 실행중에 예외가 터지기 때문에 RuntimeException 에서 사용하는 메서드들을 사용하기 위해서이다.
  • 우선 에러를 담을 Map 컬렉션을 선언해주고 생성자로 에러메시지와 에러를 담는다.
    • super(message)는 RuntimeException 의 메시지 리턴해주는 메서드를 사용하기 위해서다.
  • 그리고 실제 예외가 터지면 스프링이 아닌 사용자가 만든 예외처리 핸들러(Exception Handler)에게 권한이 가야 하기 때문에 클래스를 하나 더 만든다
  public class CustomValidationException extends RuntimeException{

    // 객체를 구분할 때 !!(JVM 이 구분한다고 한다) 중요하진않음.
    private static final long serialVersionUID = 1L;

    private Map<String,String> errorMap = new HashMap<>();

    public CustomValidationException(String message, Map<String, String> errorMap) {
        super(message);
        this.errorMap = errorMap;
    }

    public Map<String, String> getErrorMap() {
        return errorMap;
    }
}

ControllerExceptionHandler -> 모든 예외를 가로채서 로직을 실행

  • 애플리케이션 실행 중에 예외가 터지면 스프링에서 예외를 받아서 오류를 띄워주던가 한다. 하지만 ControllerExceptionHandler 가 개발자가 짜놓은 예외가 터지면 가로채서 로직을 실행한다.

@ControllerAdvice : 예외(Exception)이 발생하면 이 어노테이션이 붙은 클래스가 가로챈다.
@ExceptionHandler(예외 타입) : 예외타입이 발생하는 모든 오류를 해당 어노테이션이 붙은 메서드가 가로채서 로직을 실행한다.

@RestController
@ControllerAdvice //Exception 이 발생하면 어노테이션이 붙은 클래스가 가로챈다.
public class ControllerExceptionHandler {

    @ExceptionHandler(CustomValidationException.class) //RuntimeException 이 발생하는 모든 오류를 이 메서드가 가로챈다.
    public Map<String,String> validationException(CustomValidationException e){
        return e.getErrorMap();
    }
}
  • 이 예외처리메서드는 에러를 담은 Map을 반환하므로 사용자한테는 아래와 같이 보여진다.
profile
백엔드개발자

0개의 댓글