Validation

2024. 10. 9. 17:39Spring Framework/Web on Servlet Stack

스프링 MVC는 컨트롤러 메서드에서 유효성 검사(Validation)를 지원하며, 이를 통해 요청 데이터가 정확한지 검증할 수 있습니다. 유효성 검사는 두 가지 수준에서 적용될 수 있습니다.

  1. 메서드 파라미터 단위의 유효성 검사: @ModelAttribute, @RequestBody, @RequestPart에 적용할 수 있으며, 이러한 파라미터가 @Valid@Validated로 주석 처리되면 개별적으로 검증됩니다.
  2. 메서드 레벨의 유효성 검사: 메서드 파라미터나 메서드 자체에 @Constraint(예: @Min, @NotBlank 등)를 선언하여 검증할 수 있습니다. 메서드 검증은 메서드 파라미터뿐만 아니라 중첩된 객체의 유효성 검사까지 적용됩니다.

주요 개념

  • @Valid@Validated: 요청 데이터를 바인딩한 객체에 대해 유효성 검사를 수행합니다. 이때 Errors 또는 BindingResult 파라미터가 있으면, 컨트롤러 메서드가 호출되고 유효성 검사 오류가 전달됩니다. 그렇지 않으면 MethodArgumentNotValidException 예외가 발생합니다.
  • 메서드 검증: 파라미터나 메서드 자체에 유효성 검증 애너테이션(예: @NotNull, @Min, @NotBlank)을 직접 선언하면 메서드 단위의 유효성 검사가 수행됩니다. 이 경우 HandlerMethodValidationException이 발생할 수 있습니다.
  • 전역 Validator 설정: 전역적으로 Validator를 설정하거나 컨트롤러 단위로 @InitBinder를 통해 로컬 Validator를 설정할 수 있습니다.

1. @ValidErrors를 사용한 유효성 검사

import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class AccountController {

    @PostMapping("/accounts")
    public String handleAccount(@Valid @ModelAttribute AccountForm form, Errors errors) {
        if (errors.hasErrors()) {
            // 유효성 검사 실패 시 처리
            return "errorPage";
        }
        // 성공 시 처리 로직
        return "successPage";
    }
}

코드 설명:

  • @Valid: AccountForm 객체에 대해 유효성 검사를 수행합니다. 이 객체의 필드에 선언된 검증 애너테이션(예: @NotBlank, @Size)에 따라 유효성 검사가 실행됩니다.
  • Errors: 유효성 검사 실패 시 오류 정보를 전달하는 파라미터입니다. 유효성 검사 오류가 있으면 errors.hasErrors()true를 반환하며, 오류를 처리할 수 있습니다.

2. @RequestBody를 사용한 JSON 데이터 유효성 검사

import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @PostMapping("/users")
    public ResponseEntity<String> createUser(@Valid @RequestBody User user) {
        // 유효성 검사 통과 후 처리 로직
        return ResponseEntity.ok("User created successfully!");
    }
}

코드 설명:

  • @RequestBody: JSON 요청 본문을 User 객체로 변환한 후, 유효성 검사를 수행합니다.
  • @Valid: User 객체의 필드에 대해 유효성 검사를 적용합니다. 오류가 있을 경우 MethodArgumentNotValidException 예외가 발생합니다.

3. 메서드 수준의 유효성 검사

메서드 파라미터나 반환 값에 유효성 검사를 직접 선언할 수 있습니다.

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {

    @GetMapping("/product")
    public String getProduct(
        @RequestParam @NotBlank String name, 
        @RequestParam @Min(1) int quantity) {
        return "Product name: " + name + ", Quantity: " + quantity;
    }
}

코드 설명:

  • @NotBlank, @Min: 메서드 파라미터에 직접 유효성 검사를 선언했습니다. name은 비어 있으면 안 되며, quantity는 1 이상의 값을 가져야 합니다.
  • HandlerMethodValidationException: 유효성 검사가 실패하면 이 예외가 발생하며, 이를 통해 오류를 처리할 수 있습니다.

4. 전역 Validator 설정

전역적으로 Validator를 설정하면, 컨트롤러 전체 또는 특정 파라미터에 대해 유효성 검사를 적용할 수 있습니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public Validator getValidator() {
        return new CustomValidator();  // 커스텀 Validator 설정
    }
}

코드 설명:

  • getValidator(): 전역적으로 커스텀 Validator를 설정하는 방법입니다. 이 설정은 모든 컨트롤러에 적용됩니다.

5. 예외 처리

유효성 검사 오류가 발생했을 때, 이를 처리하는 방법 중 하나는 ResponseEntityExceptionHandler 또는 @ExceptionHandler를 사용하는 것입니다.

@ExceptionHandler를 사용한 예외 처리

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.MethodArgumentNotValidException;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
        return new ResponseEntity<>("Validation failed: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

코드 설명:

  • @ControllerAdvice: 전역 예외 처리를 담당하는 클래스입니다. 모든 컨트롤러에서 발생하는 예외를 처리할 수 있습니다.
  • @ExceptionHandler: 특정 예외(여기서는 MethodArgumentNotValidException)가 발생했을 때 이 메서드를 통해 오류 응답을 생성합니다.

요약

  • @Valid@Validated는 요청 데이터를 바인딩한 객체에 대해 유효성 검사를 수행합니다. Errors 또는 BindingResult를 함께 사용하면 유효성 검사가 실패해도 컨트롤러 메서드가 호출됩니다.
  • 메서드 수준 유효성 검사는 메서드 파라미터나 반환 값에 직접 유효성 검사를 선언할 수 있으며, HandlerMethodValidationException을 통해 오류를 처리할 수 있습니다.
  • 전역적으로 Validator를 설정하거나, @InitBinder 메서드를 통해 컨트롤러별로 커스텀 Validator를 설정할 수 있습니다.
  • 예외 처리 방식으로는 ResponseEntityExceptionHandler 또는 @ExceptionHandler를 사용하여 유효성 검사 실패 시 발생하는 예외를 처리할 수 있습니다.

이 방식은 웹 애플리케이션에서 사용자의 입력 데이터를 안전하고 정확하게 검증하는 데 매우 유용하며, 유효성 검사를 통해 비즈니스 로직에서 잘못된 입력을 방지할 수 있습니다.

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class AccountForm {

    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
    private String username;

    @NotBlank(message = "Email is required")
    private String email;

    // 기본 생성자
    public AccountForm() {}

    // 생성자
    public AccountForm(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // Getter와 Setter
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class User {

    @NotBlank(message = "Username is required")
    private String username;

    @NotBlank(message = "Password is required")
    @Size(min = 6, message = "Password must be at least 6 characters long")
    private String password;

    // 기본 생성자
    public User() {}

    // 생성자
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    // Getter와 Setter
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

참고 : https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html

'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글

Controller Advice  (0) 2024.10.09
Exceptions  (0) 2024.10.09
@InitBinder  (0) 2024.10.09
Model  (0) 2024.10.09
Handler Method : Jackson JSON  (0) 2024.10.09