24.11.21 TIL Validation

신성훈·2024년 11월 21일

TIL

목록 보기
83/162

1. Validation이란?

Validation(검증)은 클라이언트가 입력한 데이터가 서버에서 처리되기 전에 올바르고 유효한 값인지 확인하는 과정을 말합니다. 웹 애플리케이션에서는 데이터의 신뢰성과 보안을 유지하기 위해 필수적인 과정입니다.


2. Spring에서의 Validation

Spring은 javax.validationHibernate Validator(Bean Validation의 구현체)를 통해 유효성 검사를 지원합니다. 이를 사용하면 애노테이션 기반으로 간편하게 데이터를 검증할 수 있습니다.

주요 Validation 애노테이션

  • @NotNull: 값이 null이면 안 됨
  • @NotBlank: 공백 문자만으로 이루어진 값은 안 됨
  • @Size: 문자열, 배열 등의 길이를 제한
  • @Min, @Max: 숫자 값의 최소/최대값 제한
  • @Email: 올바른 이메일 형식인지 확인
  • @Pattern: 정규식을 이용한 값 검증

3. Validation 사용 방법

3.1 DTO에 애노테이션 추가

Spring에서 Validation은 주로 DTO(Data Transfer Object)에 설정됩니다.

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

public class UserRequestDTO {

    @NotBlank(message = "닉네임은 필수 입력 항목입니다.")
    @Size(max = 20, message = "닉네임은 20자를 초과할 수 없습니다.")
    private String nickname;

    @Email(message = "유효한 이메일 형식이 아닙니다.")
    private String email;

    @NotBlank(message = "비밀번호는 필수 입력 항목입니다.")
    @Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하여야 합니다.")
    private String password;

    // Getters and Setters
}

3.2 컨트롤러에서 Validation 처리

컨트롤러에서 @Valid를 사용하여 DTO 검증을 수행합니다.

import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserRequestDTO userRequestDTO) {
        return ResponseEntity.ok("회원가입 성공!");
    }
}

4. 검증 실패 처리

Spring은 검증 실패 시 자동으로 MethodArgumentNotValidException을 발생시킵니다. 이를 커스터마이징하여 사용자에게 적절한 에러 메시지를 반환할 수 있습니다.

4.1 예외 처리

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

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

5. 실무에서 Validation 적용

5.1 비즈니스 로직 검증

Validation 애노테이션으로 해결할 수 없는 경우, 커스텀 Validator를 만들어 비즈니스 로직을 검증할 수 있습니다.

5.2 커스텀 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 = PasswordValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPassword {
    String message() default "비밀번호는 숫자와 특수문자를 포함해야 합니다.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class PasswordValidator implements ConstraintValidator<ValidPassword, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        // 숫자와 특수문자를 포함해야 유효
        return value.matches(".*\\d.*") && value.matches(".*[@#$%^&+=].*");
    }
}

5.3 DTO에 커스텀 Validator 추가

@ValidPassword
private String password;

6. 마무리

  • Validation 애노테이션을 사용하니 코드가 간결해졌고, 서버에 들어오는 데이터를 체계적으로 관리할 수 있었습니다.
  • 기본 애노테이션으로 해결되지 않는 검증 로직은 커스텀 Validator로 확장할 수 있다는 점이 유용했습니다.
  • 예외 처리 방식을 구현하며, 사용자에게 구체적이고 명확한 에러 메시지를 반환하는 것이 중요하다는 점을 배웠습니다.
  • 실무에서는 DTO를 통해 클라이언트 입력 데이터와 내부 도메인을 분리하고, 이를 통해 유지보수를 더 쉽게 할 수 있음을 깨달았습니다.
profile
조급해하지 말고, 흐름을 만들고, 기록하면서 쌓아가자.

0개의 댓글