-
회원가입 유효성 검사 2Spring Boot/회원 관리 2 - 회원 가입 2021. 12. 10. 20:25
회원가입 시 유효성 검사하는 여러가지 방법을 설명한다.
유효성 검사 목표 :
username 길이를 20자 이하로 설정
일단 간단하게 회원가입 코드 구성을 보면,
<signup.jsp> 파일
<form class="login__input" action="/auth/signup" method="post"> <input type="text" name="username" placeholder="유저네임" required="required" /> <input type="password" name="password" placeholder="패스워드" required="required" /> <input type="email" name="email" placeholder="이메일" required="required" /> <input type="text" name="name" placeholder="이름" required="required"/> <button>가입</button> </form>
<SignupDto.java> 파일
import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import com.tree.sky.domain.user.User; import lombok.Data; @Data public class SignupDto { @Size(min = 2, max = 20) @NotBlank private String username; @NotBlank private String password; @NotBlank private String email; @NotBlank private String name; public User toEntity() { return User.builder() .username(username) .password(password) .email(email) .name(name) .build(); } }
signup.jsp 파일로 회원가입 창을 만들어서
사용자가 웹 사이트에서 username, password, email, name 을 입력하면
signupDto.java파일에서 그 정보들을 받아서
AuthController.java 파일에 넘겨주고,
AuthController.java 파일은 회원가입 로직을 만든다. (AuthController.java 파일은 아래에 나와있다.)
----------------------------------------------------------
그래서 이제 유효성 검사를 실행해보겠다.
목표 : username 길이를 20자 이하로 설정
1. 코드로 직접 완성
<AuthController.java 파일>
@PostMapping("/auth/signup") public String signup(SignupDto signupDto) { if(signupDto.getUsername().length()>20) { System.out.println("길이 초과했습니다."); }
이렇게, signupDto.java로 받은 회원 정보를 뽑아서 길이 20 이상이라면 길이가 초과되었다는 처리를 할 수 있을 것이다.
회원가입 창에서 20자 이상의 글자를 입력하였더니 이렇게 sts(이클립스) 콘솔창에 메세지가 나왔다.
이 방법은 코드가 복잡해지기 때문에 애노테이션을 활용해 보겠다.
2. @Valid 애노테이션 사용
@Valid 애노테이션으로 유효성 검사를 할 수 있는데,
회원가입을 처리하는 AuthController.java 파일에 사용한다.
자세히 살펴보자면
<AuthController.java 파일>
@PostMapping("/auth/signup") public String signup(@Valid SignupDto signupDto, BindingResult bindingResult) { if(bindingResult.hasErrors()) { Map<String,String> errorMap = new HashMap<>(); for(FieldError error : bindingResult.getFieldErrors()) { errorMap.put(error.getField(), error.getDefaultMessage()); System.out.println(error.getDefaultMessage()); } throw new RuntimeException("유효성 검사 실패"); }else { User user = signupDto.toEntity(); authService.회원가입(user); return "auth/signin"; } }
위에 보면 메서드 매개변수로 @Valid SignupDto signupDto 를 볼 수 있을 것이다.
이는 SignupDto.java 파일에서 유효성검사를 실시한 다음 문제가 있으면 이 메서드의
BindingResult 변수에 담아서 에러를 처리한다.
먼저 SignupDto 파일을 보자면,
import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import com.tree.sky.domain.user.User; import lombok.Data; @Data public class SignupDto { @Size(min = 2, max = 20) @NotBlank private String username; @NotBlank private String password; @NotBlank private String email; @NotBlank private String name; public User toEntity() { return User.builder() .username(username) .password(password) .email(email) .name(name) .build(); } }
이렇게 각 변수에
@NotBlank, @size 같은 애노테이션이 달려있다.
그러니깐, 사용자가 username 을 입력할 때 만약
글자값은 2자 이상 20자 이하여야만 하며, 빈칸이면 안된다.
(toEntity 메서드는 회원가입을 위한 메서드이기 때문에 일단은 무시하고 봐도 된다.)
만약 이를 어기면 AuthController.java의 bindingResult 변수에 에러가 담기고 그 에러를 처리해주면 딘다.
2가지 처리를 하였는데,
1. System.out.println(error.getDefaultMessage());
-> error 메세지를 출력하려고 했으므로 아래와 같은 메세지가 sts(이클립스) 콘솔창에 나오게 된다.
-> 이 부분은 그저 콘솔창에 나오는 것이기 때문에 크게 의미가 없어 입력하지 않아도 된다.
(사용자에게 에러메세지를 보여주는 것이 주 목적이기 때문에)
2. throw new RuntimeException("유효성 검사 실패");
아래와 같은 메세지가 사용자 화면(웹사이트)에 나오면서 여러 줄의 에러가 나타나게 된다.
그런데 이런 에러창을 사용자에게 보여주는 것은 그렇게 좋은 환경이 아니므로,
에러 처리를 우리가 직접 다양하게 해보기 위해 ControllerExceptionHandler.java 파일을 만들어 보겠다.
2. @Valid 애노테이션 사용 - 에러 처리 (RuntimeException)
<ControllerExceptionHandler.java> 파일
@RestController @ControllerAdvice public class ControllerExceptionHandler { @ExceptionHandler(RuntimeException.class) public String validationException(RuntimeException e) { return e.getMessage(); } }
@ControllerAdvice 애노테이션 : 발생하는 모든 Exception 을 낚아채도록 하는 애노테이션이다.
@RestController 애노테이션 : 에러에 대한 응답을 할 때 데이터를 리턴하도록
(예를 들어 웹 사이트상에 "에러 관련 문자" 나오도록 )
이렇게 처리한 다음에 회원가입 페이지에서 사용자가
username 값을 20자 초과해서 입력하면 다음 페이지로 넘어가면서
웹사이트상에 사용자에게 아래와 같은 메세지가 나오게 된다.
즉, AuthController.java에서
throw new RuntimeException("유효성 검사 실패");
이렇게 에러 문구를 날렸고,
ControllerExceptionHandler.java 파일에서 에러 처리 할 때
return e.getMessage()
처리를 하였기 때문에
"유효성 검사 실패" 라는 문구가 나오게 된 것이다.
그런데 우리는 이 문구가 아니라,
AuthController.java 파일에서 보면
@PostMapping("/auth/signup") public String signup(@Valid SignupDto signupDto, BindingResult bindingResult) { if(bindingResult.hasErrors()) { Map<String,String> errorMap = new HashMap<>(); for(FieldError error : bindingResult.getFieldErrors()) { errorMap.put(error.getField(), error.getDefaultMessage()); System.out.println(error.getDefaultMessage()); } throw new RuntimeException("유효성 검사 실패"); }else { User user = signupDto.toEntity(); authService.회원가입(user); return "auth/signin"; } }
여기에서 bindingResult 에서 꺼내와서
HashMap 에 담은 에러 문구를 보이게 하고 싶다.
(만약 username 이 20자 초과 하면,
@Valid 에서 bindingResult 변수에 담아주는 위와 같은 에러 문구를 리턴하고 싶은 것이다.
그런데 현재 그렇게 하지 못하는 이유는, 기본적으로 RuntimeException 이 매개변수로 String 만 받기 때문이다.
Map<String,String> errorMap = new HashMap<>();
그래서 Map 형식의
errorMap을 받지 못한다.
그래서 우리는 RuntimeException 을 상속해서 CustomValidationException.java 파일을 만들어서
이에 대한 처리를 하려고 한다.
2. @Valid 애노테이션 사용 - 에러 처리 (CustomValidationException)
<CustomValidationException.java> 파일
import java.util.Map; public class CustomValidationException extends RuntimeException{ private static final long serialVersionUID = 1L; private Map<String, String> errorMap; public CustomValidationException(String message, Map<String, String> errorMap) { super(message); this.errorMap = errorMap; } public Map<String, String> getErrorMap(){ return errorMap; } }
일단 필요한 변수는
String 형 message 변수와
Map 형 errorMap 변수이다.
그래서 원래는
private String message;
private Map<String, String> errorMap;
public CustomValidationException(String message, Map<String, String> errorMap){
this.message = message;
this.errorMap = errorMap;
}
이렇게 되어 있어야 한다.
그런데 이렇게 하지 않고 super 를 사용한 이유는,
CustomValidationException 이 RuntimeException 을 상속받고 있고,
RuntimeException 은
String message 로 메세지를 받게 되면,
이 메세지를 super(message) 를 통해 class Exception 부모한테 넘기고
넘기다 보면
결국
여기까지 넘어가게 된다.
public String getMessage() { return detailMessage; }
그래서 부모가 대신 String message를 받아주기 때문에
String message 에 대한 getter 나
private String message 변수를 따로 만들어 주지 않아도 된다.
이렇게 만든 다음에
<AuthController.java> 파일
@PostMapping("/auth/signup") public String signup(@Valid SignupDto signupDto, BindingResult bindingResult) { if(bindingResult.hasErrors()) { Map<String,String> errorMap = new HashMap<>(); for(FieldError error : bindingResult.getFieldErrors()) { errorMap.put(error.getField(), error.getDefaultMessage()); System.out.println(error.getDefaultMessage()); } throw new CustomValidationException("유효성 검사 실패", errorMap);
에러를 CustomValidationException 으로 던져주면,
<ControllerExceptionHandler.java> 파일
@ExceptionHandler(CustomValidationException.class) public Map<String, String> validationException(CustomValidationException e) { return e.getErrorMap(); }
ControllerExceptionHandler.java에서 받아서
errorMap을 화면에 나타내어준다.
그래서 회원가입 시 username 을 20 초과 해서 입력한 다면
아래와 같이 화면에 에러메세지가 나오게 된다.
문제점 : errorMap은 리턴 가능한데, "유효성 검사 실패" 라는 메세지를 띄울 수 없다.
이유는, ControllerExceptionHandler.java에 보면 리턴타입이 Map<String, String> 이기 때문이다.
그래서 "유효성 검사 실패" 와 errorMap 을 함께 띄울 수 있도록
CMRespDto.java 파일을 만든다.
2. @Valid 애노테이션 사용 - 에러 처리 (CMRespDto)
<CMRespDto.java> 파일
@AllArgsConstructor @NoArgsConstructor @Data public class CMRespDto { private String message; private Map<String, String> errorMap; }
<ControllerExceptionHandler.java> 파일
@ExceptionHandler(CustomValidationException.class) public CMRespDto validationException(CustomValidationException e) { return new CMRespDto(e.getMessage(), e.getErrorMap()); }
CMRespDto.java 파일을 만든 뒤 ControllerExceptionHandler.java 파일에 리턴 타입으로 활용하면
message와 errorMap 을 모두 나타낼 수 있게 된다.
그래서 username 값을 20자 초과해서 입력한 뒤 회원가입하면
웹 사이트가 다음 페이지로 넘어가면서 화면에 아래와 같은 오류 메세지를 나타내준다.
그런데, 여기서는 String message와 Map 타입의 errorMap 을 리턴했지만
항상 이 타입들만 리턴하는 것은 아니고 다른 곳에서도 사용할 것이므로
CMRespDto.java 파일을 변형시킨다.
@AllArgsConstructor @NoArgsConstructor @Data public class CMRespDto<T> { private int code; private String message; private T errorMap; }
여기에 code변수를 추가하였는데,
데이터 통신이 성공하면 1을, 실패하면 -1을 리턴하도록 해줄 것이다.
그리고 Map 타입의 errorMap이 아니라 다양한 타입이 가능하도록 제네릭 타입을 사용하였다.
원하는 타입으로 변형시켜주면 된다.
<ControllerExceptionHandler.java> 파일
@ExceptionHandler(CustomValidationException.class) public CMRespDto<?> validationException(CustomValidationException e) { return new CMRespDto<Map<String, String>>(-1, e.getMessage(), e.getErrorMap()); }
여기서 메서드 리턴타입을 보면 CMRespDto<?> 로 되어 있는데,
아래 리턴부분을 보면
return new CMRespDto<Map<String, String>> 으로
나와있기 때문에 스프링이 알아서 ? 대신 Map<String, String> 으로 인식해준다.
(물론 CMRespDto<?> 대신 CMRespDto<Map<String, String>> 으로 입력해도 된다.)
여기서는 회원가입이 실패하는 것이기 때문에, 응답코드(code 변수)를 -1로 리턴해주는 것이다.
그러면 username 값을 20자 초과해서 입력하고 회원 가입 하면
웹 사이트가 다음 페이지로 넘어가면서 아래와 같은 에러메세지를 보여준다.
하지만 이렇게 메세지가 나오면 사용자 입장에서는 메세지 모양도 별로 예쁘지 않고,
회원가입을 다시 하려면 뒤로가기 버튼을 눌러서 다시 처리해야하기 때문에 불편하다.
그래서 자바스크립트를 활용한 Script.java 파일을 만들어서 처리해보도록 한다.
2. @Valid 애노테이션 사용 - 에러 처리 (Script)
<Script.java> 파일
public class Script { public static String back(String msg) { StringBuffer sb = new StringBuffer(); sb.append("<script>"); sb.append("alert('"+msg+"');"); sb.append("history.back();"); sb.append("</script>"); return sb.toString(); } }
자바스크립트를 활용해서, 에러메세지 창이 생기도록 했으며, 에러메세지의 확인 버튼을 누르면 다시 회원가입 화면으로 돌아가는 코드를 만들었다.
<ControllerExceptionHandler.java> 파일
@ExceptionHandler(CustomValidationException.class) public String validationException(CustomValidationException e) { return Script.back(e.getErrorMap().toString()); }
return 에 CMRespDto가 아닌, Script를 활용하였다.
특히 errorMap을 리턴하는데, 이는 Map타입이지, String 타입이 아니므로
toString() 메서드를 활용하여 String으로 리턴될 수 있도록 하였다.
이렇게 처리한 다음
username 값을 20자 초과해서 입력하고 회원가입하면
아래와 같은 에러메세지 창이 나온다.
그리고 확인 버튼을 누르면 회원가입 화면으로 돌아가기 때문에
사용자 입장에서는 보기 좋고 편리한 에러 메세지가 된다.
여기에서 CMRespDto와 Script 로 에러메세지를 비교해 봤는데,
클라이언트 응답 : Script.java 가 좋음
Ajax 통신 : CMRespDto.java 가 좋음
예외 처리에 대한 자료를 아래 블로그에서 볼 수 있다.
https://sjh836.tistory.com/122
예외처리 (throwable, exception, error, throws)
참조문서 : https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html 1. 예외처리란? Exception Handling 이라고 하며, 잘못된 하나로 인해 전체 시스템이 무너지는 결과를 방지하기 위한 기술적인..
sjh836.tistory.com
참고 자료 : 이지업 강의 사이트 "스프링부트 SNS프로젝트 - 포토그램 만들기"
'Spring Boot > 회원 관리 2 - 회원 가입' 카테고리의 다른 글
회원가입 전처리, 후처리 (0) 2021.12.07 회원가입 (0) 2021.12.06