Spring Boot로 개발하다 보면 사용자로부터 입력받은 데이터가 항상 신뢰할 수 있는 형태로 들어오지 않는다는 걸 알게 됩니다. 여기서 필요한 것이 바로 "Validation"입니다. Spring Boot에서 Validation은 사용자가 입력한 데이터를 검증하는 중요한 기능입니다. 이를 통해 비즈니스 로직 이전에 데이터의 무결성을 보장할 수 있습니다.

Validation은 입력 데이터가 유효한지 검사하는 과정을 의미합니다.
예를 들어, 회원가입 폼에서 이메일 형식이 맞는지 확인하거나, 나이가 음수가 아닌지 검사하는 과정을 떠올리면 됩니다. 이러한 Validation은 서버에서 한 번 더 데이터를 검증하여 보안과 품질을 보장하는 데 중요한 역할을 합니다.
표준화된 방식: Java Bean Validation은 JSR 380표준을 따르므로 다양한 프레임워크에서 일관되게 사용 가능합니다.
어노테이션 기반: @NotNull, @Size 등의 어노테이션을 통해 손쉽게 검증 로직을 추가할 수 있습니다.
유효성 검사 메시지 제공: 각 검증 어노테이션에 사용자 정의 메시지를 설정해 유연하게 사용할 수 있습니다.
유연한 커스터마이징: 기본 검증 로직 외에도 Validator 인터페이스를 구현하여 커스텀 Validation을 작성할 수 있습니다.
@Valid: DTO(데이터 전달 객체)에서 Validation을 활성화하기 위해 사용됩니다.
@NotNull, @NotEmpty, @NotBlank: 각각 Null, 빈 문자열, 공백만 포함된 문자열을 허용하지 않음을 나타냅니다.
@Size: 문자열이나 컬렉션의 길이를 제한합니다.
@Pattern: 정규식을 사용하여 입력 데이터 형식을 제한합니다.
@Min, @Max: 숫자의 최소값과 최대값을 설정합니다.
@Email: 이메일 형식을 검증합니다.
Constraint: 입력값에 부과되는 제약 조건을 정의합니다.
@NotNull, @Size(min=5, max=15)@NotNull(message = "이메일은 필수 입력 값입니다.")
@Email(message = "유효한 이메일 형식이 아닙니다.")
private String email;Validator: 입력값을 검증하는 도구입니다. 기본 제공되는 표준 Validator 외에도 커스텀 Validator를 구현할 수 있습니다.
public class CustomValidator implements ConstraintValidator<CustomConstraint, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.matches("^[a-zA-Z]+$");
}
}BindingResult: Controller에서 Validation 오류를 처리하기 위해 사용되는 객체입니다.
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody UserDto userDto, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().body(bindingResult.getAllErrors());
}
return ResponseEntity.ok("회원가입 성공!");
}1. 간단한 DTO(Data Transfer Object)에 Validation 적용하기
아래는 회원가입 요청을 처리하는 DTO에서 Validation을 사용하는 예시입니다.
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class SignupRequest {
@NotBlank(message = "이름은 필수 항목입니다.")
private String name;
@Email(message = "유효한 이메일 주소를 입력하세요.")
private String email;
@Size(min = 8, message = "비밀번호는 최소 8자 이상이어야 합니다.")
private String password;
// Getters and Setters
}
2. 컨트롤러에서 Validation 활성화
컨트롤러 메서드에서 @Valid 어노테이션을 사용하여 Validation을 활성화할 수 있습니다.
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/signup")
public class SignupController {
@PostMapping
public String signup(@Valid @RequestBody SignupRequest request) {
return "회원가입이 완료되었습니다.";
}
}
3. 검증 실패 시 처리
Spring Boot는 기본적으로 검증 실패 시 MethodArgumentNotValidException을 발생시킵니다. 이를 처리하려면 Exception Handler를 작성할 수 있습니다.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.MethodArgumentNotValidException;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
String errorMessages = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ResponseEntity<>(errorMessages, HttpStatus.BAD_REQUEST);
}
}
필요한 곳에만 Validation 적용: 모든 DTO에 Validation을 적용하면 성능이 저하될 수 있습니다. 필요한 곳에서만 사용하세요.
@Pattern을 사용합니다.@Pattern(regexp = "^\d{10,11}$", message = "전화번호는 10~11자리 숫자여야 합니다.")
private String phoneNumber;Validation 범위 설정
유효성 메시지 관리: 유효성 에러 메시지는 사용자 친화적으로 작성해야 합니다. 이를 위해 messages.properties 파일을 사용해 메시지를 관리할 수 있습니다. 또한 Validation 메시지를 하드코딩하지 말고, 메시지 파일로 분리하여 다국어 지원에 대비합니다.
email.notnull=이메일은 필수 입력 값입니다.
email.invalid=유효한 이메일 형식이 아닙니다.@NotNull(message = "{email.notnull}")
@Email(message = "{email.invalid}")
private String email;Validation으로 처리하기 어려운 로직은 커스텀 Validator를 만들어 사용하세요.커스텀 Validator 예시
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPhoneNumber {
String message() default "유효하지 않은 전화번호 형식입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.matches("\\d{3}-\\d{3,4}-\\d{4}");
}
}
public class NonEmptyListValidator implements ConstraintValidator<NonEmptyList, List<?>> {
@Override
public boolean isValid(List<?> value, ConstraintValidatorContext context) {
if (value == null || value.isEmpty()) {
return false;
}
return true;
}
}Spring Boot Validation은 코드의 복잡성을 줄이고, 데이터를 안전하게 검증하는 데 유용한 도구입니다. 실무에서 활용할 때는 적절한 Validation 적용과 사용자 친화적인 에러 메시지 제공에 중점을 두어야 합니다.