Bean Validation 은 Java 에코시스템에서 유효성 검증 로직을 구현하기 위한 사실상의 표준이다. Spring 및 Spring Boot와 잘 통합되어있다. 그러나 몇 가지 함정이 있다. 이 자습서에서는 모든 주요 유효성 검사 사용 사례와 각각에 대한 예제 코드를 살펴본다.
예제 코드
이 글은 동작하는 예제 코드가 GitHub으로 함께 제공된다 .
Spring Boot Validation Starter 사용하기
Spring Boot의 Bean Validation 지원은 validation starter로 제공되며, 다음과 같이 프로젝트에 포함할 수 있다(Gradle).
implementation('org.springframework.boot:spring-boot-starter-validation')
validation starter가 하는 일은 hibernate validator 의 호환 버전에 대한 종속성만 추가해 준다. hibernate validator는 가장 널리 사용되는 Bean Validation 스펙의 구현체이다.
Bean Validation Basics
기본적으로 Bean Validation은 클래스 필드에 특정 Annotation을 지정하여 해당 필드에 대한 제약 조건을 정의함으로써 작동한다.
Common Validation Annotations
가장 일반적인 유효성 검사 애너테이션은 다음과 같다.
@NotNull
: 필드가 null이 아니어야 한다.@NotEmpty
: 목록 필드가 비어 있으면 안 된다.@NotBlank
: 문자열 필드가 빈 문자열이 아니어야 한다(즉, 하나 이상의 문자가 있어야 함).@Min
및@Max
: 숫자 필드는 값이 지정한 값보다 높거나 낮아야 한다(경계 포함).@Pattern
: 문자열 필드가 지정한 정규식과 일치하여야 한다.@Email
: 문자열 필드가 유효한 이메일 주소여야 한다.
이를 사용하는 클래스의 예는 다음과 같다:
class Customer {
@Email
private String email;
@NotBlank
private String name;
// ...
}
Validator
객체가 유효한지 확인하기 위해서는 제약 조건이 충족되는지 확인하는 Validator 에 객체를 전달한다.
Set<ConstraintViolation<Input>> violations = validator.validate(customer);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
[[Validating Programmatically]] 섹션에서 Validator
를 사용하는 더 많은 방법을 알아볼 수 있다.
@Validated and @Valid
많은 경우, Spring은 validation을 알아서 수행한다. 우리가 validator 객체를 만들 필요도 없다. 대신에, 우리는 Spring이 어떤 객체를 validation 해야 하는지 알려줘야 하는데, @Validated
와 @Valid
애너테이션을 사용한다.
@Validated
애너테이션은 클래스 레벨의 애너테이션이다. 이 애너테이션이 특정 클래스에 적용되면, Spring은 이 클래스의 메서드에 전달되는 파라미터의 유효성을 검사해야 한다는 것을 알게된다. [[validating path variables and request parameters]] 섹션에서 더 자세하게 알 수 있다.
메서드의 파라미터나 필드에 @Valid
애너테이션을 적용하면, Spring은 메서드의 파라미터나 필드의 유효성을 검사해야 한다는 것을 알게된다. [[validating a request body]] 섹션에서 더 자세하게 배울 수 있다.
Spring MVC Controller의 입력을 유효성 검사하기
Spring REST 컨트롤러를 구현했고, 클라이언트가 전달한 입력의 유효성을 검사하려고 한다고 가정하자. 유입되는 HTTP 요청에서 다음 세 가지를 검사할 수 있다:
- request body
- path variables (예:
/foos/{id}
에서id
) - query parameters
각각에 대해 더 자세히 살펴보자.
Validating a Request Body
POST 및 PUT 요청에서는 request body에 JSON 페이로드를 전달하는 것이 일반적이다. Spring은 수신한 JSON을 Java 객체에 자동으로 매핑한다. 우리는 입력 Java 객체가 요구 사항을 충족하는지 확인하려고 한다.
다음은 수신 페이로드 클래스이다:
class Input {
@Min(1)
@Max(10)
private int numberBetweenOneAndTen;
@Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
private String ipAddress;
// ...
}
int
필드는 @Min
과 @Max
를 사용하여 1~10 (경계 포함) 사이의 값을 가져야 하도록 지정하였다. String
필드는 @Pattern
을 사용하여 IP 주소가 정규식을 만족 하도록 지정하였다(이 정규식은 255 보다 큰 숫자를 허용하기 때문에 유효하지 않은 IP 주소를 허용한다. 이 문제는 이 튜토리얼의 when we’re building a custom validator에서 수정할 예정이다).
HTTP request의 body를 검증하기 위해서, REST controller의 request body에 @Valid
애너테이션을 지정한다.
@RestController
class ValidateRequestBodyController {
@PostMapping("/validateBody")
ResponseEntity<String> validateBody(@Valid @RequestBody Input input) {
return ResponseEntity.ok("valid");
}
}
Input
파라미터에 @Valid
애너테이션을 붙여주었다. 이 파라미터에는 request body로 부터 읽어들여야 한다는 것을 표시하는 @RequestBody
애너테이션도 붙어 있다. 이렇게 하면, controller의 로직에 진입하기 전에 Spring에게 해당 객체를 Validator
에 전달해야 한다고 알려줄 수 있다.
필드가 복잡한 타입이라면 @Valid 를 사용하라.
만일에
Input
클래스가 필드로 또 다른 복잡한 타입을 가지고 있고, 이 필드도 유효성 검증을 해야 한다면 해당 필드에@Valid
를 지정하면 된다.
유효성 검사에 실패하면 MethodArgumentNotValidException
이 발생한다. Spring은 기본적으로 이 exception을 HTTP 상태코드 400(Bad Request)으로 변환한다.
integration test로 동작을 검증할 수 있다:
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = ValidateRequestBodyController.class)
class ValidateRequestBodyControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@Test
void whenInputIsInvalid_thenReturnsStatus400() throws Exception {
Input input = invalidInput();
String body = objectMapper.writeValueAsString(input);
mvc.perform(post("/validateBody")
.contentType("application/json")
.content(body))
.andExpect(status().isBadRequest());
}
}
// .andDo(print())로 response 출력:
MethodArgumentNotValidException: Validation failed for argument [0] in public ResponseEntity<String> ValidateRequestBodyController.validateBody(Input) with 2 errors:
Field error in object 'input' on field 'ipAddress':
rejected value [invalid];
codes [Pattern.input.ipAddress, Pattern.ipAddress,Pattern.java.lang.String, Pattern];
arguments [
DefaultMessageSourceResolvable:
codes [input.ipAddress,ipAddress];
arguments [];
default message [ipAddress], [Ljavax.validation.constraints.Pattern$Flag; @359ceb13, ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$];
default message [must match "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"]
]
Field error in object 'input' on field 'numberBetweenOneAndTen':
rejected value [99];
codes [Max.input.numberBetweenOneAndTen, Max.numberBetweenOneAndTen, Max.int, Max];
arguments [
DefaultMessageSourceResolvable:
codes [input.numberBetweenOneAndTen, numberBetweenOneAndTen];
arguments [];
default message [numberBetweenOneAndTen],10];
default message [must be less than or equal to 10]
]
@WebMvcTest
annotation 게시물에서 Spring MVC controller를 테스트하는 방법에 대해 좀 더 자세히 알 수 있다.
Validating Path Variables and Request Parameters
path variables와 request parameters를 검증하는 방법은 약간 차이가 있다.
여기서는 복잡한 자바 객체를 검증하지는 않을 것이다. path variables와 request parameters는 int
같은 primitive 타입이거나 이에 대응되는 Integer
나 String
객체이기 때문이다.
위에서는 클래스의 필드에 애너테이션을 추가하였지만, 여기서는 컨트롤러의 메서드 파라미터에 제약 조건 애너테이션을(@Min
) 직접 지정한다.
@RestController
@Validated
class ValidateParametersController {
@GetMapping("/validatePathVariable/{id}")
ResponseEntity<String> validatePathVariable(
@PathVariable("id") @Min(5) int id
) {
return ResponseEntity.ok("valid");
}
@GetMapping("/validateRequestParameter")
ResponseEntity<String> validateRequestParameter(
@RequestParam("param") @Min(5) int param
) {
return ResponseEntity.ok("valid");
}
}
@Validated
애너테이션을 컨트롤러의 클래스 레벨에 지정해야 하는 것에 주의하라. Spring에게 메서드의 파라미터에 지정된 제약조건 애너테이션을 평가해야 한다고 알려준다.
여기서는 @Validated
애너테이션을 클래스 레벨에만 적용한다. 이 애너테이션은 메서드 레벨에도 사용할 수 있지만, 이에 대해서는 나중에 validation groups에서 배우게 된다.
앞서 request body의 유효성 검증에 실패했을 때는 MethodArgumentNotValidException
이 발생한다. 하지만 여기서는 ConstraintViolationException
이 발생한다. Spring은 이 exception에 대한 default exception handler를 등록하지 않으므로, 기본적으로 HTTP status 500(Internal Server Error)으로 응답한다.
만일 HTTP status 400으로 응답하려면 커스텀 exception handler를 컨트롤러에 추가하면 된다. 클라이언트에서 유효하지 않은 파라미터를 제공하여 오류가 발생했기 때문에 "bad request"로 응답하는 것은 적절해 보인다.
@RestController
@Validated
class ValidateParametersController {
// request mapping method omitted
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>("not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
이 튜토리얼의 뒷 부분에서는, 클라이언트가 점검할 수 있도록 실패한 모든 validation에 대한 세부 정보를 응답에 포함하는 structured error response 를 반환하는 방법을 살펴본다.
integration test로 validation 동작을 확인할 수 있다:
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = ValidateParametersController.class)
class ValidateParametersControllerTest {
@Autowired
private MockMvc mvc;
@Test
void whenPathVariableIsInvalid_thenReturnsStatus400() throws Exception {
mvc.perform(get("/validatePathVariable/3"))
.andExpect(status().isBadRequest());
}
@Test
void whenRequestParameterIsInvalid_thenReturnsStatus400() throws Exception {
mvc.perform(get("/validateRequestParameter")
.param("param", "3"))
.andExpect(status().isBadRequest());
}
}
Spring의 서비스 메서드의 입력 검증하기
Spring 컴포넌트의 입력값도 검증할 수 있다. 그러기 위해서는 @Validated
와 @Valid
애너테이션을 함께 사용해야 한다.
@Service
@Validated
class ValidatingService{
void validateInput(@Valid Input input){
// do something
}
}
한 번 더 말하지만, @Validated
애너테이션은 클래스 레벨에서만 평가되므로, 이 유즈케이스에서는 메서드 레벨에 지정하지 않는다.
validation 동작을 확인하는 테스트이다:
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ValidatingServiceTest {
@Autowired
private ValidatingService service;
@Test
void whenInputIsInvalid_thenThrowsException(){
Input input = invalidInput();
assertThrows(ConstraintViolationException.class, () -> {
service.validateInput(input);
});
}
}
Validating JPA Entities
validation을 위한 마지막 방어선은 퍼시스턴스 레이어이다. Spring Data는 Bean Validation을 지원하는 Hibernate를 기본적으로 사용한다.
퍼시스턴스 레이어가 Validation에 적합한 장소인가?
일반적으로 우리는 퍼시스턴스 레이어에 도달할 만큼 늦은 시점에 validation을 하고싶어 하지 않는다. 왜냐하면 상위 계층의 비즈니스 코드가 잠재적으로 유효하지 않은 객체들과 함께 처리되었으며, 이 객체들은 예기치 않은 오류를 야기시킬 수 있다는 의미이기 때문이다. 이 주제에 대해서는 나의 다른 글(Bean Validation anti-patterns)에서 보다 자세히 다룬다.
Input
클래스의 인스턴스를 데이터베이스에 저장하려 한다고 가정하자. 먼저 필요한 JPA 애너테이션인 @Entity
와 ID 필드를 추가한다.
@Entity // add
public class Input {
// add
@Id
@GeneratedValue
private Long id;
@Min(1)
@Max(10)
private int numberBetweenOneAndTen;
@Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
private String ipAddress;
// ...
}
그런 다음, Spring Data repository를 생성한다. 이는 Input
객체에 대한 저장(persist)과 조회(query) 기능을 제공해준다.
public interface ValidatingRepository extends JpaRepository<Input, Long> {}
기본적으로, 제약조건 애너테이션을 위반하는 Input
객체를 repository에서 저장할 때 마다 ConstraintViolationException
이 발생한다. 다음 integration test가 이를 보여준다:
@ExtendWith(SpringExtension.class)
@DataJpaTest
class ValidatingRepositoryTest {
@Autowired
private ValidatingRepository repository;
@Autowired
private EntityManager entityManager;
@Test
void whenInputIsInvalid_thenThrowsException() {
Input input = invalidInput();
assertThrows(ConstraintViolationException.class, () -> {
// 동일 동작 - save()
repository.save(input);
entityManager.flush(); // test 코드라서 명시적으로 호출하는 코드를 삽입함
// 동일 동작 - save() & flush()
//repository.save(input);
//repository.flush();
// 동일 동작 - saveAndFlush()
// repository.saveAndFlush(input);
});
}
}
Spring Data repository를 테스트하는 것에 대해서는 나의 글(@DataJpaTest
annotation)에서 자세히 다루고 있다.
주의할 것은, Bean Validation은 Hibernate가 EntityManager
를 flush 시킬 때에만 트리거된다. Hibernate는 특정한 상황이 되면 EntityManager
를 자동으로 flush 시키지만, 이 integration test의 경우에는 수동으로 호출해줘야 한다.
어떤 이유에서든 Spring Data repository의 Bean Validation을 비활성화하려면, Spring Boot 프로퍼티인 spring.jpa.properties.javax.persistence.validation.mode
를 none
으로 설정하면 된다.
A Custom Validator with Spring Boot
사용할 수 있는 constraint annotations이 우리의 유즈케이스를 만족할 수 없다면, 직접 만들 수도 있다.
위에서 살펴본 Input
클래스에서, 우리는 String이 유효한 IP 주소인지 검증하는 정규 표현식을 사용했다. 그러나 그 정규 표현식은 완벽하지 않다. 255 보다 큰 숫자도 허용한다(예: "111.111.111.333"은 유효한 것으로 판정됨).
정규식을 사용는 대신에 자바로 체크하는 validator를 구현하여 이 문제를 해결해보자(보다 복잡한 정규 표현식으로 해결할 수도 있지만, 우리는 자바로 검증하는 것을 더 선호한다).
먼저, IpAddress
라는 커스텀 애너테이션을 만든다.
@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = IpAddressValidator.class)
@Documented
public @interface IpAddress {
String message() default "{IpAddress.invalid}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
커스텀 애너테이션은 다음 항목들이 필요하다:
message
attribute:ValidationMessage.properties
의 프로퍼티 key를 지정한다. 제약 위반시 메시지를 식별하는데 사용된다.groups
attribute: 유효성 검사가 트리거되는 상황을 정의하는데 사용된다(validation groups 에서 좀 더 자세하게 다룬다).payload
attribute: 이 유효성 검사와 함께 전달될 페이로드를 정의한다(거의 사용되지 않는 기능이므로, 이 튜토리얼에서는 다루지 않는다).@Constraint
:ConstraintValidator
인터페이스의 구현체를 지정한다.
validator 구현체는 다음과 같다:
class IpAddressValidator implements ConstraintValidator<IpAddress, String> {
private static final Pattern pattern = Pattern.compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Matcher matcher = pattern.matcher(value);
try {
if (!matcher.matches()) {
return false;
} else {
for (int i = 1; i <= 4; i++) {
int octet = Integer.parseInt(matcher.group(i));
if (octet > 255) {
return false;
}
}
return true;
}
} catch (Exception e) {
return false;
}
}
}
이제 다른 제약조건 애너테이션과 똑같은 방식으로 @IpAddress
애너테이션을 사용할 수 있다.
class InputWithCustomValidator {
@IpAddress
private String ipAddress;
// ...
}
Validating Programmatically
Spring의 내장 Bean Validation 지원에 의존하는 대신, 프로그래밍 방식으로 유효성 검사를 호출하는 유즈케이스가 있을 수 있다. 이 경우 Bean Validation API를 직접 사용할 수 있다.
Validator
인스턴스를 생성한 후 validate
메서드를 호출하여 유효성검사를 트리거 할 수 있다.
class ProgrammaticallyValidatingService {
void validateInput(Input input) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Input>> violations = validator.validate(input);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
이 방법은 Spring 지원이 전혀 필요하지 않다.
하지만 Spring Boot는 사전 구성된 Validator
인스턴스를 제공하기 때문에, 우리가 직접 생성할 필요가 없다. 제공되는 인스턴스를 우리의 서비스에 주입해서 사용하면 된다.
@Service
class ProgrammaticallyValidatingService {
private final Validator validator;
ProgrammaticallyValidatingService(Validator validator) {
this.validator = validator;
}
void validateInputWithInjectedValidator(Input input) {
Set<ConstraintViolation<Input>> violations = validator.validate(input);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
Spring이 이 서비스의 인스턴스를 생성하면서 자동으로 생성자를 통해 Validator
를 주입한다.
다음 테스트가 위의 두 메소드가 예상대로 동작하는지 검증한다:
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ProgrammaticallyValidatingServiceTest {
@Autowired
private ProgrammaticallyValidatingService service;
@Test
void whenInputIsInvalid_thenThrowsException(){
Input input = invalidInput();
assertThrows(ConstraintViolationException.class, () -> {
service.validateInput(input);
});
}
@Test
void givenInjectedValidator_whenInputIsInvalid_thenThrowsException(){
Input input = invalidInput();
assertThrows(ConstraintViolationException.class, () -> {
service.validateInputWithInjectedValidator(input);
});
}
}
Using Validation Groups to Validate Objects Differently for Different Use Cases
종종 어떤 객체는 여러 유즈 케이스에서 공유된다.
일반적인 CRUD 작업의 예를 살펴보자: "Create"와 "Update" 유즈 케이스는 입력으로 동일한 객채 타입을 사용할 가능성이 높다.그러나 상황에 따라 트리거되어야 하는 유효성 검사가 있을 수도 있다.
- "Create" 유즈 케이스에서만
- "Update" 유즈 케이스에서만
- 두 유즈 케이스 모두에서
이와 같은 유효성 검증 규칙을 구현할 수 있는 Bean Validation 기능을 "Validation Groups"라고 한다.
우리는 이미 모든 제약 조건 애너테이션이 groups
필드를 가져야 한다는 것을 살펴본 적이 있다. 트리거 되어야 하는 특정한 유효성 검사 그룹을 각각 정의하는 어떤 클래스들을 전달하는데 사용할 수 있다.
우리의 CRUD 예제를 위해 OnCreate
와 OnUpdate
마커 인터페이스를 정의하자:
interface OnCreate {}
interface OnUpdate {}
이제 이 마커 인터페이스들을 제약 조건 애너테이션과 함께 사용할 수 있다.
class InputWithGroups {
@Null(groups = OnCreate.class)
@NotNull(groups = OnUpdate.class)
private Long id;
// ...
}
이렇게 설정하면 id는 "Create" 유즈 케이스에서는 비어 있어야 하고, "Update" 유즈 케이스에서는 비어있지 않은지 확인할 수 있다.
Spring은 @Validated
애너테이션에서 validation groups을 지원한다.
@Service
@Validated
class ValidatingServiceWithGroups {
@Validated(OnCreate.class)
void validateForCreate(@Valid InputWithGroups input){
// do something
}
@Validated(OnUpdate.class)
void validateForUpdate(@Valid InputWithGroups input){
// do something
}
}
@Validated
애너테이션은 클래스 레벨에 적용되어야 하는 것에 주의하라. 어떤 validation group이 활성화되어야 하는지는 메서드 레벨에 적용해야 한다.
위의 내용이 예상대로 동작하는지 확인하기 위해 unit test를 구현한다:
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ValidatingServiceWithGroupsTest {
@Autowired
private ValidatingServiceWithGroups service;
@Test
void whenInputIsInvalidForCreate_thenThrowsException() {
InputWithGroups input = validInput();
input.setId(42L);
assertThrows(ConstraintViolationException.class, () -> {
service.validateForCreate(input);
});
}
@Test
void whenInputIsInvalidForUpdate_thenThrowsException() {
InputWithGroups input = validInput();
input.setId(null);
assertThrows(ConstraintViolationException.class, () -> {
service.validateForUpdate(input);
});
}
}
Validation Group은 주의해서 사용하자
validation groups를 사용하는 것은 관심사(concern)가 섞이기 때문에 안티패턴이 되기 쉽다. validation groups를 사용하여 검증되어야 하는 인티티(객체)는 모든 유즈 케이스(gorups)에 대한 유효성 검증 규칙을 알아야 하기 때문이다. 이 주제에 대해서는 내 글인 Bean Validation anti-patterns에서 자세히 다루고 있다.
Handling Validation Errors
유효성 검증이 실패하면 클라이언트에 의미있는 에러 메시지를 반환하고 싶다. 클라이언트가 도움이 되는 에러 메시지를 표시할 수 있게 하려면, 우리는 실패한 각각의 유효성 검증의 에러 메시지를 포함하는 데이터 구조를 반환해야 한다.
먼저 이 데이터 구조를 정의해야 한다. Violation
객체를 리스트로 가지고 있는 ValidationErrorResponse
클래스를 정의한다:
public class ValidationErrorResponse {
private List<Violation> violations = new ArrayList<>();
// ...
}
public class Violation {
private final String fieldName;
private final String message;
// ...
}
그런 다음, 컨트롤러 레벨에서 발생하는 모든 ConstraintViolationException
을 처리할 전역 ControllerAdvice
를 작성한다. request bodies에서 발생하는 유효성 검증 에러도 잡아낼 수 있도록 MethodArgumentNotValidException
도 처리한다:
@ControllerAdvice
class ErrorHandlingControllerAdvice {
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ValidationErrorResponse onConstraintValidationException(ConstraintViolationException e) {
ValidationErrorResponse error = new ValidationErrorResponse();
for (ConstraintViolation violation : e.getConstraintViolations()) {
error.getViolations().add(
new Violation(violation.getPropertyPath().toString(), violation.getMessage()));
}
return error;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ValidationErrorResponse onMethodArgumentNotValidException(MethodArgumentNotValidException e) {
ValidationErrorResponse error = new ValidationErrorResponse();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
error.getViolations().add(
new Violation(fieldError.getField(), fieldError.getDefaultMessage()));
}
return error;
}
}
여기서는 예외로 부터 위반 결과를 꺼내서 우리가 정의한 ValidationErrorResponse
데이터 구조로 변환하는 것만 수행하였다.
@ControllerAdvice
애너테이션은 애플리케이션 컨텍스트 내에 있는 모든 컨트롤러에서 발생하는 예외를 전역적으로 처리할 수 있게 해준다.
Conclusion
이 튜토리얼에서, Spring Boot로 애플리케이션을 만들 때 필요한 주요 validation 기능들을 살펴보았다.
github repository에서 예제 코드를 살펴볼 수 있다.
'잡다구리' 카테고리의 다른 글
resilience4j: CircuitBreaker configuration 간단 정리 (0) | 2022.08.18 |
---|---|
p8s: Spring Boot에서 Prometheus 메트릭 노출하기 (0) | 2022.08.18 |
jpa: 컬럼의 네이밍 컨벤션 전략 - physical-strategy (0) | 2022.08.16 |
Spring: @DataJpaTest (0) | 2022.08.14 |
Spring: @ConfigurationProperties (0) | 2022.08.14 |
기초부터 다지는 ElasticSearch 운영 노하우 : 스터디 기록 (0) | 2022.08.13 |
Hexagonal Architecture with Java and Spring (0) | 2022.08.13 |
Building a Multi-Module Spring Boot Application with Gradle (0) | 2022.08.13 |
댓글