inblog logo
|
LifeLog, DevLog
    Spring

    알고 있어야할 어노테이션들 - 2

    예외처리, 검증
    KYJTHEYJ's avatar
    KYJTHEYJ
    Dec 25, 2025
    알고 있어야할 어노테이션들 - 2
    Contents
    알고 있어야할 어노테이션들 - 2예외처리 관련검증 관련

    알고 있어야할 어노테이션들 - 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 이 발생함

    💡

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

    Share article
    Contents
    알고 있어야할 어노테이션들 - 2예외처리 관련검증 관련

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog