Form 태그 입력결과를 비밀번호 해시까지 진행 후 DB에 저장하였다.
입력 ID에 길이 제한을 두거나, Unique한 ID만 들어오게 하려면 어떻게 해야할까?
20자 이하인 경우는 1번에서 전처리로 처리 할 수 있다.
하지만 unique 한지 확인하려면 JPA를 통해 DB를 거쳐야 하므로 2번의 후처리를 통해서만 처리 가능하다.
전처리는 보통 Validation 으로, 후처리는 Exception Handler를 통해 처리하는데 이에 대해 알아보자.
AuthController 의 @PostMapping 부분.
if문 추가로 username 길이 검증을 진행한다.
Controller가 지저분해지는 문제가 있다.
1) porm.xml에 Spring-boot-Validation 라이브러리를 추가한다.
2) signupDto에 제약조건들을 지정한다. (Validation)
Spring-boot-Validation의 어노테이션들
Annotation | 조건 |
---|---|
@Notnull | null 불가 |
@Null | null만 가능 |
@NotEmpty | 빈 문자열 불가 |
@Size(min =, max = ) | 문자열, 배열의 크기 제약조건 |
@Pattern(regex=) | 정규식 만족여부 |
@Max(숫자) | 지정 값 이하만 가능 |
@Min(숫자) | 지정 값 이상만 가능 |
@Positive | 양수만 가능 |
@PositiveOrZero | 양수 or 0만 가능 |
@Negative | 음수만 가능 |
@NegativeOrZero | 음수 or 0만 가능 |
이메일 형식만 가능 | |
@DecimalMax(value= ) | 지정된 값(실수) 이하만 가능 |
@DecimalMin(value= ) | 지정된 값(실수) 이상만 가능 |
@AssertFalse | false만 가능 |
@AssertTrue | true만 가능 |
3) User에서 DB 삽입시 제약조건도 지정한다.(JPA)
4) Controller 에서 검증 오류시 처리를 지정한다.
5) 20글자 넘는 Username 삽입 시 오류 메시지 출력
상단의 FieldError DefaultMessage와. 하단의 User의 JPA로 DB제약조건을 걸어놓아 DB삽입시 에러를 뱉는 것을 확인할 수 있다.
@Valid를 통해 검증을 추가하고 검증 오류 발생시 별도의 처리를 할 수 있게 되었다. 이 때 오류 발생시는 @ResponseBody로 메시지를 반환하고, 오류가 없다면 뷰를 넘겨주려면 어떻게 해야할까?
@ResponseBody를 메서드에 추가하면 모든 반환이 Body 반환이 되기에 ViewResolver가 동작하지 않게된다. 따라서 오류 발생시 Exception 을 던지고 처리해야한다.
1) Controller 에서 Valid 위반 존재시 RuntimeException을 던진다
2) ControllerExceptionHandler 클래스를 생성한다.
@ControllerAdvice 어노테이션을 추가하면 모든 Excepction을 가로챈다.
@ExceptonHandler 어노테이션을 추가하면 해당 Exception 발생시 처리를 지정할 수 있다.
여기서는 e.getMessage()를 리턴하는데 @RestController 기에 뷰 반환이 아닌 메시지 자체를 사용자에게 전송하게된다.
수행 결과이다. 유효성검사 실패함이 아닌 HashMap의 ErrorField별 메시지를 반환하려면 어떻게 해야할까? Exception 의 매개변수로는 String 밖에 넘길 수 없다.
bindingResult 에 error 가 존재한다면 RuntimeException을 던졌다.
ControllerExceptionHandler 클래스에서 @ControllerAdvice로 모든 Exception을 가로채서 RuntimeException 이면 Exception.getMessage로 예외메시지를 반환하도록 해주었다.
그런데 Exception 객체는 String 밖에 담을 수 없어 예외메시지 밖에 반환할 수 없다. error 내용을 반환하려면 어떻게 해야할까?
1) RuntimeException을 상속하는 CustomValidationException 클래스를 생성한다.
CustomValidationException 클래스는 필드로 errorMap 객체를 갖는다.
2) ExceptionHandler 가 메시지가 아닌 errorMap 객체를 반환하도록 한다.
3) Controller 에서 에러 존재시 RuntimeException 이 아닌 CustomValidationException을 던진다. 이때 errorMap을 같이 넘겨준다.
errorMap 을 반환하고, 메시지 컨버터가 Json으로 변환하여 출력된 것을 볼 수 있다.
필드 : 에러메시지가 반환되었다.
예외 발생 시 해시맵인 errorMap을 반환하며, 필드 : 에러미시지 형태로 반환이 진행된다.
이때 "유효성검사 실패함" 메시지와 errorMap을 함께 출력하고 싶은데 이럴땐 어떻게 해야할까?
ControllerExceptionHandler 의 반환형이 Map<String, String>이므로 현재는 errorMap만 반환 가능한 상태이다. 따라서 둘 다 출력하기 위한 새로운 Dto를 만들어줘야 한다.
1) CMRespDto 클래스를 생성한다. 필드로 String 과 Map<String, String>을 갖는다.
2) ControllerExceptionHandler 에서 CMRespDto 객체를 반환하도록 변경한다.
message와 errorMap이 동시에 출력된다.
메시지와 errorMap을 동시에 출력할 수 있게 되었다.
errorMap 말고 User 같은 다른 타입의 데이터를 출력하고 싶을 땐 어떻게 해야할까?
1) CMRespDto 에서 제네릭 추가. data의 타입은 제네릭으로 결정.
2) ControllerExceptionHandler 에서 제네릭 타입을 결정하여 사용한다. errorMap은 Map<String,String> 을, String 이면 <String>으로 제네릭 결정.
반환형은 <?> 으로 제네릭 추론 사용.
제네릭으로 <String>을 사용하면 문자열도 잘 반환하는 모습이다.
이제 회원가입 validation 처리결과를 code, message, 그리고 원하는 데이터를 CMRespDto 객체에 담아 반환할 수 있게 되었다.
그런데 이게 사용자 입장에서는 상당히 불편하다. JS를 활용한 alert가 표시되도록 변경해보자.
1) Script 클래스의 back 함수를 생성한다. back 함수는 String 을 받아 이를 alert 로 출력하고 뒤로가기 시키는 js 코드 문자열을 반환하는 함수읻.
2) ControllerExceptionHandler 에서 Script.back 메서드 수행결과 js 코드를 반환한다.
3) 수행결과
수행결과 제대로 errorMap을 출력하는 JS코드가 브라우저로 반환되어 경고창을 출력하는 것을 볼 수 있다.
Ajax나 Android 같이 응답결과를 개발자가 사용할 경우 CMRespDto 로 결과를 반환하는 것이.
클라이언트에게 결과를 전송할 경우 Script 로 결과를 반환하는 것이 유용하다.