요르딩딩

@ExceptionHandler, @Valid 본문

[Web]/[Spring]

@ExceptionHandler, @Valid

요르딩딩 2022. 12. 27. 10:09
728x90
반응형

이번시간에는 요청파라미터의 @Valid를 활용한 유효성 체크와 @ExceptionHandler를 활용한 예외처리에 대해 공부해보겠습니다.

우선, @Valid를 사용하기 위해 pom.xml에 아래 라이브러리를 추가해줍니다.

<!-- hibernate-validator 5~6 버전대랑 javax.validation 1.x.x는 호환이 안된다. -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.10.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

그리고, 유효성 체크를 진행할 DTO에 어노테이이션을 적용하여, 체크해줍니다.

다만, collection은 체크를 해주지 않으므로, 추가로 @Valid 어노테이션을 붙여주어야합니다.

package com.**.controller.request;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class TransferRequest {

	@NotBlank
	private String accountKey;
	@Positive
	private int companyNo;
    
	@NotNull
	@Valid
	private List<Accounts> depAccts;

	@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
	public static class Accts {
		@Positive
		private int employeeNo;
		@NotBlank
		private String bankCode;

		public int getEmployeeNo() {
			return employeeNo;
		}

		public String getBankCode() {
			return bankCode;
		}
	}

	public String getAccountKey() {
		return accountConnectKey;
	}

	public void setAccountKey(String accountKey) {
		this.accountKey = accountKey;
	}

	public int getCompanyNo() {
		return companyNo;
	}

	public void setCompanyNo(int companyNo) {
		this.companyNo = companyNo;
	}
}

유효성 체크를 할 객체에 @Valid를 붙여줍니다.

이제 유효성 체크에 걸리게 되면 Exception이 발생하게 됩니다.

이때 아래 주석처럼 해당 컨트롤러내에 다이렉트로 적용시켜줄 수도 있지만, global하게 처리하도록 해보겠습니다.

package com.**.controller;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
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;

import javax.validation.Valid;


@RestController
@RequestMapping("/svc/salary")
public class SalaryController {

	protected Logger logger = LogManager.getLogger(this.getClass());
	
	@Autowired
	private SalaryService SalaryService;

	@PostMapping("/reservation")
	public Result SalaryReservation(@Valid @RequestBody TransferRequest transferRequest) throws Exception {
		Result result = new Result();
		result = SalaryService.SalaryReservation(transferRequest);
		return result;
	}
	
	@PostMapping("/result")
	public LuluResult SalaryResult(@Valid @RequestBody TransferRequest transferRequest) throws Exception {
		Result result = new Result();
		result = SalaryService.SalaryResult(transferRequest);
		return result;
	}
	
//	/*
//	 * 예외처리
//	 */
//	@ExceptionHandler(MethodArgumentNotValidException.class)
//	public ErrorResponse processValidationError(MethodArgumentNotValidException exception) {
//		BindingResult bindingResult = exception.getBindingResult();
//
//		ErrorResponse errorResponse = new ErrorResponse();
//		
//		StringBuffer errorMsg = new StringBuffer();
//		for (FieldError fieldError : bindingResult.getFieldErrors()) {
//			errorMsg.append(fieldError.getField()).append(", ");
//		}
//		
//		errorResponse.setResultCode(400);
//		errorResponse.setResultMsg("[" + errorMsg.substring(0, errorMsg.length()-2).toString() + "] 필수파라미터 체크 바랍니다.");
//
//		return errorResponse;
//	}


}

 

exceptionAdvisor에 @RestControllerAdvice 어노테이션을 활용해 Exception을 커스텀하여 처리하도록 하겠습니다.

아래 코드는 특정 컨트롤러만 적용할 수 있도록 basePackageClasses를 적용하였고, 아래와 같이 필수 파라미터를 체크하는 예외를 커스텀 하여 적용하였습니다. 

 

여기서 특정회사의 프레임워크를 사용할 경우, 예외 발생시 해당 프레임워크의 예외처리가 먼저 적용되어 아래로직까지 오지 않는 경우가 발생 할 수 있으므로, @Order 어노테이션을 활용하여, 가장먼저 적용되도록 적용시켜주어야 합니다.

 

HTTP status Code에 적용하기 위해서는 @ResponseStatus(HttpStatus.BAD_REQUEST)을 추가해 주어야합니다.

package com.**.common.exceptionAdvisor;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice(basePackageClasses = SalaryBatchController.class) // body 사용가능
@Order(Ordered.HIGHEST_PRECEDENCE) // ruruExcptrion 보다 먼저
public class ExceptionAdvisor {
	protected Logger logger = LogManager.getLogger(this.getClass());

    @ResponseStatus(HttpStatus.BAD_REQUEST) // HTTP Status Code 적용
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ErrorResponse processValidationError(MethodArgumentNotValidException exception) {
		BindingResult bindingResult = exception.getBindingResult();

		ErrorResponse errorResponse = new ErrorResponse();

		StringBuffer errorMsg = new StringBuffer();
		for (FieldError fieldError : bindingResult.getFieldErrors()) {
			errorMsg.append(fieldError.getField()).append(", ");
		}

		errorResponse.setResultCode(HttpStatus.BAD_REQUEST.value());
		errorResponse.setResultMsg("[" + errorMsg.substring(0, errorMsg.length() - 2).toString() + "] 필수파라미터 체크 바랍니다.");

		return errorResponse;
	}

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // HTTP Status Code 적용
	@ExceptionHandler({ Exception.class })
	protected ErrorResponse handleServerException(Exception exception) {

		ErrorResponse errorResponse = new ErrorResponse();
		errorResponse.setResultCode(HttpStatus.BAD_REQUEST.value());
		errorResponse.setResultMsg(exception.getMessage());
		return errorResponse;
	}
}
728x90
반응형

'[Web] > [Spring]' 카테고리의 다른 글

Spring - Interceptor  (0) 2022.11.11
Spring - Filter  (0) 2022.11.11
[Spring] branch 생성 및 적용하는법  (0) 2022.09.20
JUnit 테스트하는 방법  (0) 2022.09.14
DTO를 사용해보자  (0) 2022.08.12
Comments