0. gradle에 의존성을 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-validation'
1. UserController
- DTO 형태로 Request를 받는다.
- @RequestBody와 @Valid
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {
private final UserService userService;
@PostMapping("/signup")
public ResponseEntity<BaseResponse> signup(@Valid @RequestBody SignupRequestDto signupRequestDto) {
return userService.signup(signupRequestDto);
}
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequestDto loginRequestDto) {
return userService.login(loginRequestDto);
}
}
2. SignupRequestDto 작성
- @Email로 email형태인지 거를 수 있다.
- 그외에 @NotNull, @NotBlack를 사용하여 null과 공백을 거른다.
- message에 validation에서 걸러질 때 표출할 메세지까지 설정해준다.
public class SignupRequestDto {
@NotBlank(message = "userId는 공백이 될 수 없습니다.")
@NotNull(message = "userId는 null이 될 수 없습니다.")
@Email(message = "userID는 email 형식이어야 합니다.")
private String userId;
@NotBlank(message = "password는 공백이 될 수 없습니다.")
@NotNull(message = "password는 null이 될 수 없습니다.")
@Size(min = 4, max = 12, message = "password 최소 4자리, 최대 12자리입니다.")
private String password;
@NotBlank(message = "name은 공백이 될 수 없습니다.")
@NotNull(message = "name은 null이 될 수 없습니다.")
@Size(min = 2, max = 10, message = "name은 최소 2자리, 최대 10자리입니다.")
private String name;
}
3. GlobalExceptionHandler 작성
- GlobalExceptionHandler 클래스에 @RestControllerAdvice를 달아준다. > 전역으로 에러를 처리해준다.
- @ExceptionHandler(MethodArgumentNotValidException.class)를 통해 ValidException을 전역으로 처리해준다.
- ResponseEntity와 BaseResponse를 작성해서 구현하였다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<BaseResponse> responseValidation(MethodArgumentNotValidException e){
BindingResult result = e.getBindingResult();
String msg = result.getFieldError().getDefaultMessage();
return ResponseEntity
.status(e.getStatusCode())
.body(BaseResponse.builder()
.resultCode(e.getStatusCode().value())
.resultMsg(msg)
.build());
}
}
@Getter
@Builder
public class BaseResponse {
private final int resultCode;
private final String resultMsg;
public static ResponseEntity<BaseResponse> toResponseEntity(ErrorCode errorCode){
return ResponseEntity
.status(errorCode.getHttpStatus())
.header("Content-Type", "application/json;charset=UTF-8" )
.body(BaseResponse.builder()
.resultCode(errorCode.getHttpStatus().value())
.resultMsg(errorCode.getMsg())
.build());
}
public static ResponseEntity<BaseResponse> toResponseEntity(SuccessCode successCode) {
return ResponseEntity
.status(successCode.getHttpStatus())
.header("Content-Type", "application/json;charset=UTF-8" )
.body(BaseResponse.builder()
.resultCode(successCode.getHttpStatus().value())
.resultMsg(successCode.getDetail())
.build());
}
}
4. 실행결과
- 이메일 형식에 어긋났을 때
5. 테스트 코드 작성
- 이메일 형식이 아닌 형태로 요청을 보냈을 때 에러가 발생하는 테스트 진행
- Validation 테스트의 경우
- @BeforeAll 에서 validator를 만들어주고 사용해야한다
- @AfterAll에서는 validatorFactory.close()수행
@SpringBootTest
class UserControllerTest {
private static ValidatorFactory validatorFactory;
private static Validator validator;
@BeforeAll
public static void init() {
validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@AfterAll
public static void close() {
validatorFactory.close();
}
@Test
void testSignup_userIdValid_400() {
//given
SignupRequestDto signupRequestDto = new SignupRequestDto();
signupRequestDto.setPassword("1234");
signupRequestDto.setName("일반유저");
//when
signupRequestDto.setUserId("Usernaver.com");
//then
Set<ConstraintViolation<SignupRequestDto>> violations = validator.validate(signupRequestDto);
ConstraintViolation<SignupRequestDto> passwordViolation = violations.stream()
.filter(violation -> violation.getPropertyPath().toString().equals("userId"))
.findFirst()
.orElse(null);
assertEquals("userID는 email 형식이어야 합니다.", passwordViolation.getMessage());
}
}
6. 참조
1)https://www.baeldung.com/spring-boot-bean-validation
2)https://docs.spring.io/spring-framework/reference/core/validation/beanvalidation.html