1. 개요

사용자 입력의 유효성을 검사 할 때 Spring Boot 는이 일반적이지만 중요한 작업에 대한 강력한 지원을 제공합니다.

Spring Boot는 사용자 정의 유효성 검사기와의 완벽한 통합을 지원하지만 유효성 검사를 수행하는 사실상의 표준Bean 유효성 검사 프레임 워크 의 참조 구현 인 Hibernate Validator 입니다.

이 튜토리얼에서는 Spring Boot에서 도메인 객체의 유효성을 검사하는 방법을 살펴 보겠습니다 .

2. Maven 의존성

이 경우 기본 REST 컨트롤러를 구축하여 Spring Boot에서 도메인 객체의 유효성을 검사하는 방법을 배웁니다 .

컨트롤러는 먼저 도메인 개체를 가져온 다음 Hibernate Validator로 유효성을 검사하고 마지막으로 메모리 내 H2 데이터베이스에 유지합니다.

프로젝트의 의존성은 상당히 표준입니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> 
<dependency> 
    <groupId>com.h2database</groupId> 
    <artifactId>h2</artifactId>
    <version>1.4.197</version> 
    <scope>runtime</scope>
</dependency>

위와 같이 REST 컨트롤러를 생성하는 데 필요하므로 pom.xml 파일 spring-boot-starter-web포함 시켰 습니다. 또한 Maven Central 에서 최신 버전의 spring-boot-starter-jpaH2 데이터베이스 를 확인하십시오.

Boot 2.3부터는 spring-boot-starter-validation 의존성 도 명시 적으로 추가해야합니다 .

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-validation</artifactId> 
</dependency>

3. 간단한 도메인 클래스

프로젝트의 의존성이 이미 배치 된 상태에서 다음으로 사용자를 모델링하는 역할을하는 예제 JPA 엔티티 클래스를 정의해야합니다.

이 클래스를 살펴 보겠습니다.

@Entity
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    
    @NotBlank(message = "Name is mandatory")
    private String name;
    
    @NotBlank(message = "Email is mandatory")
    private String email;
    
    // standard constructors / setters / getters / toString
        
}

User 엔티티 클래스 의 구현은 실제로 매우 빈약하지만 Bean Validation의 제약 조건을 사용하여 이름이메일 필드 를 제한하는 방법을 간단히 보여줍니다 .

단순화를 위해 @NotBlank 제약 조건 만 사용하여 대상 필드를 제한했습니다. 또한 message 속성으로 오류 메시지를 지정했습니다 .

따라서 Spring Boot가 클래스 인스턴스의 유효성을 검사 할 때 제한된 필드 는 null이 아니어야하며 잘린 길이는 0보다 커야합니다 .

또한 Bean Validation@NotBlank 외에 많은 다른 편리한 제약 조건을 제공합니다  . 이를 통해 제한된 클래스에 다른 유효성 검사 규칙을 적용하고 결합 할 수 있습니다. 자세한 내용은 공식 Bean 유효성 검사 문서를 참조하십시오 .

사용자를 메모리 내 H2 데이터베이스에 저장하기 위해 SpringData JPA사용할 것이므로 User 객체 에 대한 기본 CRUD 기능을 갖기위한 간단한 저장소 인터페이스도 정의해야 합니다.

@Repository
public interface UserRepository extends CrudRepository<User, Long> {}

4. REST 컨트롤러 구현

물론 User 객체의 제한된 필드에 할당 된 값을 가져올 수있는 레이어를 구현해야  합니다.

따라서 유효성을 검사하고 유효성 검사 결과에 따라 몇 가지 추가 작업을 수행 할 수 있습니다.

Spring Boot는 REST 컨트롤러 구현을 통해 복잡해 보이는 프로세스를 정말 간단 하게 만듭니다 .

REST 컨트롤러 구현을 살펴 보겠습니다.

@RestController
public class UserController {

    @PostMapping("/users")
    ResponseEntity<String> addUser(@Valid @RequestBody User user) {
        // persisting the user
        return ResponseEntity.ok("User is valid");
    }
    
    // standard constructors / other methods
    
}

A의 스프링 REST 컨텍스트 의 구현 ADDUSER () 방법은 매우 표준이다.

물론 가장 관련성이 높은 부분은 @Valid 어노테이션을 사용하는 것입니다 .

Spring Boot가 @Valid 어노테이션이 달린 인수를 찾으면 자동으로 기본 JSR 380 구현 인 Hibernate Validator를 부트 스트랩하고 인수의 유효성을 검사합니다.

대상 인수가 유효성 검사를 통과하지 못하면 Spring Boot는 MethodArgumentNotValidException 예외를 throw합니다 .

5. @ExceptionHandler 어노테이션

addUser () 메서드 에 전달 된 User 객체를 자동으로 확인하는 Spring Boot를 사용하는 것이 정말 편리하지만 이 프로세스의 누락 된 측면은 유효성 검사 결과를 처리하는 방법입니다.

@ExceptionHandler의 어노테이션은 우리가 하나의 방법으로 예외 지정 유형을 처리 할 수 있습니다.

따라서 유효성 검사 오류를 처리하는 데 사용할 수 있습니다.

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(
  MethodArgumentNotValidException ex) {
    Map<String, String> errors = new HashMap<>();
    ex.getBindingResult().getAllErrors().forEach((error) -> {
        String fieldName = ((FieldError) error).getField();
        String errorMessage = error.getDefaultMessage();
        errors.put(fieldName, errorMessage);
    });
    return errors;
}

처리 할 예외로 MethodArgumentNotValidException 예외를 지정 했습니다 . 결과적으로 Spring Boot는 지정된 User 객체가 유효하지 않을 때이 메소드를 호출합니다 .

이 메서드는 Map 에있는 각 유효하지 않은 필드의 이름 및 사후 유효성 검사 오류 메시지를 저장합니다 . 그런 다음 추가 처리를 위해 Map 을 JSON 표현으로 클라이언트에 다시 보냅니다 .

간단히 말해 REST 컨트롤러를 사용하면 다른 엔드 포인트에 대한 요청을 쉽게 처리하고, 사용자 개체를 확인 하고, JSON 형식으로 응답을 보낼 수 있습니다.

이 디자인은 Thymeleaf 와 같은 템플릿 엔진에서 Angular 와 같은 완전한 기능을 갖춘 JavaScript 프레임 워크에 이르기까지 여러 웹 계층을 통해 컨트롤러 응답을 처리 할 수있을만큼 유연 합니다.

6. REST 컨트롤러 테스트

통합 테스트 를 통해 REST 컨트롤러의 기능을 쉽게 테스트 할 수 있습니다 .

UserController 인스턴스 및 MockMvc 객체 와 함께 UserRepository 인터페이스 구현을 모의 / 자동 연결하기 시작하겠습니다 .

@RunWith(SpringRunner.class) 
@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {

    @MockBean
    private UserRepository userRepository;
    
    @Autowired
    UserController userController;

    @Autowired
    private MockMvc mockMvc;

    //...
    
}

웹 레이어 만 테스트하기 때문에 @WebMvcTest 어노테이션을 사용합니다 . MockMvcRequestBuildersMockMvcResultMatchers 클래스로 구현 된 정적 메서드 집합을 사용하여 요청 및 응답을 쉽게 테스트 할 수 있습니다.

이제 요청 본문에 전달 된 유효하고 잘못된 User 객체를 사용 하여 addUser () 메서드를 테스트 해 보겠습니다 .

@Test
public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception {
    MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8"));
    String user = "{\"name\": \"bob\", \"email\" : \"bob@domain.com\"}";
    mockMvc.perform(MockMvcRequestBuilders.post("/users")
      .content(user)
      .contentType(MediaType.APPLICATION_JSON_UTF8))
      .andExpect(MockMvcResultMatchers.status().isOk())
      .andExpect(MockMvcResultMatchers.content()
        .contentType(textPlainUtf8));
}

@Test
public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception {
    String user = "{\"name\": \"\", \"email\" : \"bob@domain.com\"}";
    mockMvc.perform(MockMvcRequestBuilders.post("/users")
      .content(user)
      .contentType(MediaType.APPLICATION_JSON_UTF8))
      .andExpect(MockMvcResultMatchers.status().isBadRequest())
      .andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory")))
      .andExpect(MockMvcResultMatchers.content()
        .contentType(MediaType.APPLICATION_JSON_UTF8));
    }
}

또한 Postman 과 같은 무료 API 수명주기 테스트 애플리케이션을 사용하여 REST 컨트롤러 API 테스트 할 수 있습니다 .

7. 샘플 애플리케이션 실행

마지막으로 표준 main () 메서드를 사용하여 예제 프로젝트를 실행할 수 있습니다 .

@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Bean
    public CommandLineRunner run(UserRepository userRepository) throws Exception {
        return (String[] args) -> {
            User user1 = new User("Bob", "bob@domain.com");
            User user2 = new User("Jenny", "jenny@domain.com");
            userRepository.save(user1);
            userRepository.save(user2);
            userRepository.findAll().forEach(System.out::println);
        };
    }
}

예상대로  콘솔에 두 개의 User 개체가 인쇄되는 것을 볼 수 있습니다.

유효한 User 개체 를 사용하여 http : // localhost : 8080 / users 끝점에 대한 POST 요청 은 "User is valid" 문자열 을 반환합니다 .

마찬가지로 이름이메일 값이 없는 User 개체가 있는 POST 요청 은 다음 응답을 반환합니다.

{
  "name":"Name is mandatory",
  "email":"Email is mandatory"
}

8. 결론

이 기사에서는 Spring Boot에서 유효성 검사를 수행하는 기본 사항을 배웠습니다 .

평소처럼이 문서에 표시된 모든 예제는 GitHub에서 사용할 수 있습니다 .