Validation in Spring Boot | Baeldung
Learn how to validate domain objects in Spring Boot using Hibernate Validator, the reference implementation of the Bean Validation framework.
www.baeldung.com
A Simple Domain Class
- 사용자를 모델링하는 JPA entity class 정의
Bean Validation의 제약 조건을 사용하여 이름과 이메일 필드 제한
@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
}
@NotBlank 제약 조건
- 대상 필드 제한(message 속성을 사용하여 오류 메시지 지정)
- @NotNull과 다르게 빈 문자열이나 공백 문자열도 포함하지 않는다.
@Repository
public interface UserRepository extends CrudRepository<User, Long> {}
사용자 정보를 메모리 내 H2 데이터베이스에 저장하기 위해 Spring Data JPA를 사용할 것이므로, User 객체에 대한 기본 CRUD 기능을 제공하는 간단한 리포지토리 인터페이스를 정의해야 한다.
REST Controller 구현
User 객체의 제약 조건이 있는 필드에 할당된 값을 가져올 수 있는 레이어를 구현해야 한다.
(이를 통해 값을 검증하고, 검증 결과에 따라 추가 작업을 수행할 수 있다.)
@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
}
addUser(@Valid @RequestBody User user)
- 메서드의 인수로 전달된 객체자 검증되어야 함을 나타낸다. (여기서는 User 객체가 검증된다.)
- @RequestBody : 요청 본문에 포함된 JSON 데이터를 User 객체로 변환한다.
ResponseEntity<String>
- HTTP 응답 본문에 문자열 메시지를 포함하는 ResponseEntity 객체를 반환한다.
- ResponseEntity.ok("User is valid") : 검증이 성공하면 "User is Valid" 메시지를 포함한 HTTP 200 응답을 반환한다.
@Valid
Spring Boot가 @Valid가 달린 인수를 찾으면 기본 JSR 380의 구현체인 Hibernate Validator를 자동으로 부트스트랩하고 인수를 검증한다.
대상 인수가 검증을 통과하지 못하면, Spring Boot는 MethodArgumentNotValidException 예외를 던진다.
The @ExceptionHandler Annotation
@ExceptionHandler을 사용하면 하나의 메서드를 통해 지정된 유형의 예외를 처리할 수 있다.
@ResponseStatus(HttpStatus.BAD_REQUEST) // 클라이언트에 400 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;
}
- 지정된 User 객체가 유효하지 않은 경우 Spring Boot는 이 메서드를 호출한다.
- 검증 오류 메시지를 처리하고 이를 Map에 저장하여 JSON 형식으로 클라이언트에 반환한다. (어떤 필드에서 어떤 오류가 발생했는지 쉽게 알 수 있다.)
- MethodArgumentNotValidException.class : Spring Boot가 @Valid로 검증된 인수가 유효하지 않을 때 발생한다.
REST Controller Test
먼저 UserRepository 인터페이스 구현을 mocking/autowiring하고, UserController 객체와 MockMvc 객체를 설정한다.
@RunWith(SpringRunner.class)
@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@MockBean
private UserRepository userRepository;
@Autowired
UserController userController;
@Autowired
private MockMvc mockMvc;
//...
}
- 웹 레이어만 테스트하기 때문에 @WebMvcTest 어노테이션을 사용
요청 본문에 유효한 User 객체와 유효하지 않은 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\"}"; // 유효한 사용자 객체를 요청 본문으로 전달하여 '/users' 경로로 POST 요청을 보낸다.
mockMvc.perform(MockMvcRequestBuilders.post("/users")
.content(user)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk()) // 응답이 200 OK 상태인지 확인
.andExpect(MockMvcResultMatchers.content()
.contentType(textPlainUtf8)); // 응답 본문의 콘텐츠 타입이 text/plain; charset=UTF-8 인지 확인
}
@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()) // 응답이 400 Bad Request 상태인지 확인
.andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory"))) // JSON 응답에서 이름 필드의 오류 메시지가 "Name is mandatory"인지 확인
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.APPLICATION_JSON_UTF8)); // 응답 본문의 콘텐츠 타입이 application/json; charset=UTF-8 인지 확인
}
Postman과 같은 무료 API 라이프사이클 테스트 애플리케이션을 사용하여 REST 컨트롤러 API를 테스트할 수 있다.
Running the Sample Application
@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 객체를 포함하여 http://localhost:8080/users 엔드포인트로 POST 요청을 보내면 문자열 "User is valid"가 반환된다.
마찬가지로, 이름과 이메일 값이 없는 User 객체를 포함하여 POST 요청을 보내면 다음과 같은 응답이 반환된다.
{
"name":"Name is mandatory",
"email":"Email is mandatory"
}
'Spring' 카테고리의 다른 글
[Spring] Maven과 Gradle (0) | 2024.07.08 |
---|---|
[Spring] Exception Handling (0) | 2024.07.05 |
Validating Form Input (0) | 2024.07.02 |
Serving Web Content with Spring MVC (0) | 2024.06.28 |
Mapping Requests (0) | 2024.06.26 |