회원가입에서 유저 정보를 받아올때, 아이디,패스워드,이메일,성별 등의 유효성 검증은 필수적입니다.
아이디의 최소/최대 글자 수는 충족하였는지, 패스워드에서 특수문자와 영문자는 포함되었는지 등을 검증하는 로직이 필요합니다.
이에 대한 검증 로직을 모두 추가해주어야 하는 두려움이 발생하였지만, 그 두려움도 잠시
스프링에서 유효성 검증을 해결해주는 dependency를 찾았습니다!!
제가 구현한 검증 로직은 아래와 같습니다.
1. validation dependency 추가하기
implementation 'org.springframework.boot:spring-boot-starter-validation'
이전에는 spring-boot-starter-web 의존성 내부에 validation이 있었지만,
spring boot 2.3 version 이상부터는 아예 모듈로 빠져 validation 의존성을 따로 추가해줘야 사용할 수 있다고 합니다.
2. 어노테이션 추가하기
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class MemberDTO {
private Long id;
@NotBlank(message = "아이디는 필수 입력 값입니다.")
@Size(min= 4, max= 10, message = "아이디는 최소 4자 이상 최대 10자 이하입니다.")
private String username;
@NotBlank(message = "비밀번호는 필수 입력 값입니다.")
@Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}", message = "비밀번호는 8~20자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
private String password;
}
유효성 검증은 어노테이션을 추가하여 메세지를 전달하거나, 정규식을 통해 입력값을 검증하는 방식으로 이루어집니다.
만약, Gender 와 같이 enum클래스를 정의하여 유저정보를 받는 경우,
@NotBlank 와 같이 String을 다루는 어노테이션을 사용할 수 없으니, 서버 측에서 따로 예외처리를 해주거나,
다른 적절한 어노테이션을 찾아서 사용하셔야 합니다.
(클래스를 검증할 수 있는 어노테이션이 없음... 보통 String이나 int를 검증합니다)
회원가입으로 다양한 유저정보가 입력되는 만큼, 어노테이션의 종류도 다양합니다.
그 어노테이션 종류는 아래와 같습니다.
1. 필드 값의 null 여부
@NotNull: 필드가 null이 아닌 값을 반드시 가져야 함.
@NotBlank: 필드가 null이 아니고, 최소한 하나 이상의 공백이 아닌 문자를 포함해야 함.
@NotEmpty: 컬렉션, 배열, 문자열 등의 필드가 비어 있지 않음을 보장.
2. 숫자 값 제한
@Min(value): 필드 값이 지정된 최소값 이상이어야 함.
@Max(value): 필드 값이 지정된 최대값 이하여야 함.
@Digits(integer, fraction): 필드 값이 지정된 정수 자리수와 소수 자리수를 가져야 함.
3. 문자열 길이 및 패턴
@Size(min, max): 문자열 또는 컬렉션의 길이나 크기가 지정된 범위 내에 있어야 함.
@Pattern(regex): 필드 값이 제공된 정규 표현식과 일치해야 함.
@Email: 필드 값이 올바른 이메일 형식이어야 함.
4. 날짜와 시간
@Past: 필드 값이 과거의 날짜/시간이어야 함.
@Future: 필드 값이 미래의 날짜/시간이어야 함.
5. 논리값 검사
@AssertTrue: 필드 값이 true여야 함.
@AssertFalse: 필드 값이 false여야 함.
6. 특별한 값 검사
@CreditCardNumber: 필드 값이 올바른 신용카드 번호 형식이어야 함.
7. 객체 및 클래스 레벨 검사
@Valid: 중첩 객체나 프로퍼티에 대해 유효성 검사를 트리거.
@Validated: 클래스 또는 메서드 레벨에서 특정 유효성 검사 그룹을 지정하여 검사.
3. Controller에서 예외처리해주기
@PostMapping("/api/user")
public ResponseEntity<ResponseMsg> join(@RequestBody @Valid MemberDTO memberDTO, Errors errors){
//회원가입 유효성 검사 실패 시
if(errors.hasErrors()){
Map<String, String> validatorResult = memberService.validateHandling(errors);
ResponseMsg errorResponse = new ResponseMsg(400, "Validation Failed", validatorResult);
return ResponseEntity.badRequest().body(errorResponse);
}
memberService.save(memberDTO);
ResponseMsg responseMsg = new ResponseMsg(200, "ok", null);
return ResponseEntity.ok(responseMsg);
}
@Valid 어노테이션 설정하는건 잊지마시고!
저는 에러메세지를 던져주기 위한 클래스인 ResponseMsg를 생성해서 에러를 던져주고 있습니다!
그러고 에러메세지를 핸들링하는 로직은 서비스 단에서 따로 구현하여 아래와 같이 사용하고 있습니다.
에러메세지를 해시값으로 저장하여 관리하였습니다
// 회원가입 시, 유효성 체크
public Map<String, String> validateHandling(Errors errors) {
if (!errors.hasErrors()) {
// Errors가 없을 경우 빈 Map 반환
//외부에서 수정못하도록 불변객체로 변환
return Map.of();
}
Map<String, String> validatorResult = new HashMap<>();
for (FieldError error : errors.getFieldErrors()) {
validatorResult.put(error.getField(), error.getDefaultMessage());
}
//외부에서 수정못하도록 불변 객체로 변환
return Collections.unmodifiableMap(validatorResult);
}
아이디 값을 3자리로 주고 회원가입 요청을 보내면,
아래와 같은 에러 메세지를 확인할 수 있습니다!