ㅅㅇ

[Spring Boot] 유효성 검사 & 데이터 검증 Vaildation 본문

SW_STUDY/SpringBoot

[Spring Boot] 유효성 검사 & 데이터 검증 Vaildation

SO__OS 2023. 1. 28. 01:09

유효성 검사 & 데이터 검증 Vaildation 이란?

  • 서비스의 비즈니스 로직이 올바르게 동작하기 위해 사용되는 데이터에 대한 사전 검증하는 작업
  • 유효성 검사 혹은 데이터 검증이라고 부르는데, 흔히 Validation 이라고 부름
  • 들어오는 데이터에 대해 개발자가 의도한 값이 의도한 형식의 값으로 제대로 들어오는지 체크하는 과정
  • 데이터의 검증은 여러 계층에서 발생하는 흔한 작업
  • 기존 Validation의 여러 문제가 있는데, 이를 해결하기 위해 Java에서 Bean Validation, Hibernate Validator 를 제공함
  • Bean Validation 은 어노테이션을 통해 다양한 데이터를 검증할 수 있게 기능을 제공
  • Hibernate Validator는 Bean Validation 명세에 대한 구현체
  • Spring Boot 의 유효성 검사 표준은 Hibernate Validator 이다.
  • 3 버전부터 starter-validation 을 추가해야 함.

Validation 관련 어노테이션

어노테이션 의미
@Size 문자의 길이 조건
@NotNull null 값 불가
@NotEmpty null 값 불가 + “” 값 불가
@NotBlank null 값 불가 + “” 값 불가 + “ “ 값 불가
@Past 과거 날짜
@PastOrPresent 과거 날짜 + 오늘 날짜
@Future 미래 날짜
@FutureOrPresent 미래 날짜 + 오늘 날짜
@Pattern 정규식을 통한 조건
@Max 최대값 조건 설정 (문자열 길이 제한 가능)
@Min 최소값 조건 설정
@AssertTrue/AssertFalse 참/거짓 조건 설정
@Valid 해당 객체의 유효성 검사

 

EX ) 회원 가입 시, 이메일 양식 유효성 검사

  • pom.xml
	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  • DTO 에 검증 기준 어노테이션 설정
package io.csy.hot.model.dto;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@ToString
public class SignUpDTO {
		
//	@NotBlank // null, 빈 문자열 (스페이스 포함X) 불가
	@NotEmpty(message = "이메일을 입력하지 않았습니다.") // null, 빈 문자열, 스페이스만 포함한 문자열 불가
    @Email(message = "이메일 형식이 맞지 않습니다.") // 이메일 형식만 가능
	private String accountEmail;
	
	@NotEmpty(message = "비밀번호을 입력하지 않았습니다.") // null, 빈 문자열, 스페이스만 포함한 문자열 불가
    @Pattern(regexp="[a-zA-Z1-9]{6,12}", message = "비밀번호는 영어와 숫자로 포함해서 6~12자리 이내로 입력해주세요.")
	private String accountPassword;
	
    @NotBlank(message = "이름을 입력하지 않았습니다.")
    @Size(min = 2, max = 8, message = "이름을 2 ~ 8 자 사이로 입력해주세요.")
	private String accountName;
	
	@NotNull // null 불가능
	private String accountBirth;
	
	@NotNull
	private String accountAddress;
	
	@NotNull
	@Pattern(regexp = "^01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$", message = "전화번호 형식이 맞지 않습니다.")
	private String accountPhoneNumber;
	
	@NotNull
	private String accountGender;
	
}
  • 컨트롤러에 적용.
    들어오는 데이터 SignUpDTO signUp 매개변수에 @Valid 어노테이션을 붙이기
package io.csy.hot.controller;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.csy.hot.model.AccountService;
import io.csy.hot.model.dto.AccountDTO;
import io.csy.hot.model.dto.SignUpDTO;

@RestController
@RequestMapping("auth")
@CrossOrigin(origins = "*", allowedHeaders = "*")
public class AccountController {

	@Autowired
	private AccountService accountService;

	@PostMapping("/sing-up")
	public void signUp(@Valid @RequestBody SignUpDTO signUp) throws Exception {

		AccountDTO account = accountService.signUp(signUp);

	}

}

 

 

데이터 유효성 검사 예외 처리

  • 유효성 @Vaild 유효성 검사 후 에러 발생 시, 클라이언트에게 에러 응답을 보내는 처리이다.
  • 데이터 유효성 에러 시, MethodArgumentNotValidException 의 예외 발생
  • ResponseEntityExceptionHandler 의 handleMethodArgumentNotValid 을 오버라이딩하여 처리
  • 로그백으로 로그 남기는 것도 포함되어 있음. (빼도 무관)
  • DTO 에 작성한 각 message 들고 오는 법

String detailMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();

 

  • 아래 코드는 MethodArgumentNotValidException 예외에 대한 처리에 관하여 글로벌핸들러 코드만 나와 있는 것.
import java.nio.file.AccessDeniedException;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
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 org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler{

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    
	protected ResponseEntity<Object> handleMethodArgumentNotValid(
			MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

    	CommonErrorCode errorCode = CommonErrorCode.METHOD_ARGUMENT_NOT_VALID;
    	String detailMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
    	
		LOGGER.warn("HandleHttpRequestMethodNotSupportedException:CLIENT REQUEST ERROR:INVALID_METHOD: {}", ex.getMessage());

		return ErrorResponse.toDataVaildErrorResponse(errorCode, detailMessage, request.getDescription(false).substring(4));	}

}
  • ex) 에러 응답 예시
{
    "timestamp": "Fri Feb 03 01:04:30 KST 2023",
    "status": 400,
    "error": "METHOD_ARGUMENT_NOT_VALID",
    "message": "데이터유효성 검사 결과, 규칙에 맞지 않는 데이터입니다. : 전화번호 양식이 맞지 않습니다.",
    "path": "/auth/sing-up"
}