Validated 의 복합 예시는 추후 다른 포스트에서 알아보려한다

알고 있어야할 어노테이션들 - 2
예외처리 관련
@ExceptionHandler
어떠한 예외가 발생했을 때 이 메서드로 처리하도록 유도
(Spring 이 만들어주는 try-catch)
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
// UserNotFoundException 발생 가능
}
// 발생한 예외를 이 부분으로 처리하도록 유도
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(UserNotFoundException e) {
return new ErrorResponse(e.getMessage());
}
}
- 동작
1. getUser() 실행
↓
2. UserNotFoundException 발생
↓
3. Spring이 @ExceptionHandler 찾음
↓
4. handleNotFound() 실행
↓
5. ErrorResponse 반환 (JSON)@RestControllerAdvice
모든 컨트롤러의 예외를 처리하도록 유도 (ControllerAdvice + ResponseBody)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(UserNotFoundException e) {
return new ErrorResponse(e.getMessage());
}
}
// @ControllerAdvice 는 View 를 반환
// 범위 지정하기
// 특정 패키지만
@RestControllerAdvice("com.example.api")
// 특정 컨트롤러만
@RestControllerAdvice(assignableTypes = {UserController.class})검증 관련
@Valid
객체의 검증 요청 (내부 필드 등에 걸어진 검증 어노테이션들을 통하여)
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserRequest request) {
return userService.create(request);
}
// UserRequest 내부의 검증 요청
public class UserRequest {
@NotBlank // ← 이게 검증됨
private String name;
@Email // ← 이것도 검증됨
private String email;
}@NotNull, @NotBlank, @Email, @Min, @Max 등등…
public class UserRequest {
@NotNull(message = "이름은 필수입니다") // null 불가
private String name;
@NotBlank // null, "", " " 모두 불가
private String nickname;
@NotEmpty // null은 아니지만 "" 불가 " " 허용
private String hobby;
@Email
private String email;
@Min(0)
@Max(150)
private int age;
}
// @AssertTrue -> 해당 값이 true 인지 검증
// @Size(min = XXX, max = XXX) -> 해당 값이 주어진 값 사이에 해당하는지 검증
// @Pattern(regexp = 정규식) -> 정규식 검증
// MethodArgumentNotValidException 어긋나면 발생@Validated
Spring 전용의 검증 어노테이션
그룹 검증을 지원하고, 메서드 파라미터의 검증도 지원함
// Valid의 경우
// DTO
public class UserRequest {
@NotBlank // 항상 검증됨
private String name;
@NotBlank // 항상 검증됨
private String email;
@NotNull // 항상 검증됨
private Long id;
}
// Controller
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserRequest request) {
// 모든 필드 검증
// id도 검증됨 (생성 시에는 불필요한데...)
}
@PutMapping("/users/{id}")
public User updateUser(@Valid @RequestBody UserRequest request) {
// 모든 필드 검증
// 생성과 수정이 같은 검증 규칙...
}이러한 상황일 경우 같은 필드를 같은 검증 규칙으로 생성과 수정을 동일하게 함
다른 상황이여야 할 경우엔 불가능함 (Valid의 기능을 수정하는 것은 불가)
// Validated 로 그룹 정의 하여 검증하기
// ========== 그룹 정의 ==========
public interface CreateGroup {}
public interface UpdateGroup {}
// ========== DTO ==========
public class UserRequest {
@NotNull(groups = UpdateGroup.class) // 수정 시만 검증
private Long id;
@NotBlank(groups = {CreateGroup.class, UpdateGroup.class}) // 둘 다
private String name;
@NotBlank(groups = CreateGroup.class) // 생성 시만 검증
@Email(groups = {CreateGroup.class, UpdateGroup.class})
private String email;
@NotBlank(groups = CreateGroup.class) // 생성 시만 검증
@Size(min = 8, groups = CreateGroup.class)
private String password;
}
// ========== Controller ==========
@PostMapping("/users")
public User createUser(
@Validated(CreateGroup.class) @RequestBody UserRequest request
) {
// CreateGroup만 검증:
// - name ✅
// - email ✅
// - password ✅
// - id ❌ (검증 안 함)
return userService.create(request);
}
@PutMapping("/users/{id}")
public User updateUser(
@Validated(UpdateGroup.class) @RequestBody UserRequest request
) {
// UpdateGroup만 검증:
// - id ✅
// - name ✅
// - email ✅
// - password ❌ (검증 안 함)
return userService.update(request);
}클래스 레벨에서 검증또한 지원한다
// Service에 @Validated 붙이기
@Service
@Validated // ← 클래스 레벨!
public class UserService {
// ✅ 메서드 파라미터 검증 가능!
public User createUser(
@NotBlank String name,
@Email String email,
@Min(0) int age
) {
// 파라미터 자동 검증!
return userRepository.save(new User(name, email, age));
}
// ✅ 반환값 검증도 가능!
@NotNull
public User findById(@Min(1) Long id) {
return userRepository.findById(id)
.orElseThrow();
}
}
// 검증 실패시는 ConstraintViolationException 이 발생함💡
Share article