프로그래밍/Spring & Spring boot

[Spring boot] RESTAPI _ 04 Exception & validation(데이터 검증)

pupu91 2022. 11. 3. 18:23
반응형

Validation

클라이언트의 데이터는 조작이 쉽고, 모든 데이터가 정상적인 방식으로 들어오지 않을 가능성도 있기 때문에
Client Side뿐만 아니라 Server Side에서도 데이터 유효성 검사를 할 필요가 있다.
=>Spring boot starter validation 활용

사용 방법
1. 의존성 추가
2. Controller의 유효성 검사 적용 Request 객체 앞에 @Validated 어노테이션 추가
3. Request를 핸들링 할 객체 정의 시 @Validated 어노테이션을 통해 필요한 유효성 검사 적용

 

Validation Test


 

pom.xml 에 라이브러리 추가
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

 

 

 

UserDTO

@NotNull : null 허용하지 않음. "", " " 허용
@NotBlank :  null, "", " " 모두 허용하지 않음
@Size : (min= , max=) 길이 제한, @Min, @Max로도 표현 가능
@Past : 현재보다 과거
@Future : 현재보다 미래

public class UserDTO {
	
	private int no;
	
	@NotNull(message = "아이디는 반드시 입력되어야 합니다.")  
	@NotBlank(message = "아이디는 공백일 수 없습니다.")   
	private String id;
	private String pwd;
	
	@NotNull(message = "이름은 반드시 입력되어야 합니다.")
	@Size(min=2, message="이름은 2글자 이상 입력해야 합니다.") 
	private String name;
	
	@Past
	private java.util.Date enrollDate;         
    
	public UserDTO() {}
	
	public UserDTO(int no, String id, String pwd, String name, Date enrollDate) {
		super();
		this.no = no;
		this.id = id;
		this.pwd = pwd;
		this.name = name;
		this.enrollDate = enrollDate;
	}

	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public java.util.Date getEnrollDate() {
		return enrollDate;
	}
	public void setEnrollDate(java.util.Date enrollDate) {
		this.enrollDate = enrollDate;
	}
	
	@Override
	public String toString() {
		return "UserDTO [no=" + no + ", id=" + id + ", pwd=" + pwd + ", name=" + name + ", enrollDate=" + enrollDate
				+ "]";
	}
	
}

 

 

 

 

ErrorResponse
public class ErrorResponse {
	
	private String code;
	private String description;
	private String detail;
	
	public ErrorResponse(String code, String description, String detail) {
		super();
		this.code = code;
		this.description = description;
		this.detail = detail;
	}
	
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public String getDetail() {
		return detail;
	}
	public void setDetail(String detail) {
		this.detail = detail;
	}
	
	@Override
	public String toString() {
		return "ErrorResponse [code=" + code + ", description=" + description + ", detail=" + detail + "]";
	}
	
	
}

 

 

 

ExceptionController
 
@ControllerAdvice : 컨트롤러를 보조해주는 기능을 제공한다는 것을 명시 전역 예외처리를 등록할 수 있다.
@ControllerAdvice 
public class ExceptionController {

	@ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserResistException(UserNotFoundException e){
		String code = "ERROR_CODE_00000";
		String description = "회원 정보 조회 실패";
		String detail = e.getMessage();
		
		return new ResponseEntity<>(new ErrorResponse(code, description, detail), HttpStatus.NOT_FOUND);    	
    }
	
    /* 유효성이 통과되지 못하는 오류가 발생하면 동작하는 메소드*/
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity<ErrorResponse> methodValidException(MethodArgumentNotValidException e){
		
		String code ="";
		String description = "";
		String detail = "";
		
		/* 에러가 있다면 */
		if(e.getBindingResult().hasErrors()) {
			detail = e.getBindingResult().getFieldError().getDefaultMessage();
			
			String bindResultCode = e.getBindingResult().getFieldError().getCode();
			System.out.println(bindResultCode);
			
			switch(bindResultCode) {
			case "NotNull":
				code = "ERROR_CODE_00001";
				description = "필수 값이 누락되었습니다.";
			case "NotBlank":
				code = "ERROR_CODE_00002";
				description = "필수 값이 공백으로 처리되었습니다.";	
			case "Size":
				code = "ERROR_CODE_00003";
				description = "알맞은 크기의 값이 입력되지 않았습니다.";		
			}
		}
		
	      ErrorResponse errorResponse = new ErrorResponse(code, description, detail);
	      
	      return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);

		
	}    
}

 

 

반응형

 

UserNotFoundException
public class UserNotFoundException extends Exception {

	public UserNotFoundException(String msg) {
		super(msg); //최종적으로 Exception객체 쪽에 저장되게 만든다
	}
	
}

 

 

 

ValidTestController
import java.net.URI;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/valid")
public class ValidTestController {
	
    /*1. 유저조회- Exception Test*/
	@GetMapping("/users/{userNo}")
	public ResponseEntity<?> findUserByNo() throws UserNotFoundException{
		
		boolean check = true;
		if(check) {
			throw new UserNotFoundException("회원 정보를 찾을 수 없습니다.");			
		}
		
		return ResponseEntity.ok().build();
	}

    /* 2. 유저 등록 - valid Test*/
	@PostMapping("/users")
	public ResponseEntity<?> registUser(@Validated @RequestBody UserDTO user){
		
		System.out.println(user);
		
		return ResponseEntity
				.created(URI.create("/valid/users/" + "userNo"))
				.build();
	}

 

 

 

 

POSTMAN - 유저 조회 (Exception Test)

 

 

 

 

POSTMAN - 유저 등록 (Valid Test)

반응형