본문 바로가기
카테고리 없음

[Spring] Contoller단에서 Validation

by hbIncoding 2024. 4. 8.

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

 

Java Bean Validation :: Spring Framework

Bean Validation provides a common way of validation through constraint declaration and metadata for Java applications. To use it, you annotate domain model properties with declarative validation constraints which are then enforced by the runtime. There are

docs.spring.io