반응형
반응형
해당 글에서는 Controller에서 발생하는 Exception을 Global Exception을 구성하여서 처리하는 방법에 대해서 공유합니다.
1) 개발 환경
💡 Global Exception을 적용하는데 활용한 개발환경입니다.
개발환경 | 버전 |
java | 1.8 |
Spring Boot | 2.7.4 |
빌드관리도구 | Gradle 7.5 |
개발 툴 | IntelliJ IDEA 2022.3 |
2) Controller 내에서 발생하는 Exception 처리 전체적 흐름
💡@RestControllerAdvice / @ExceptionHandler를 이용한 Controller 내에서 Exception 처리에 대한 전체적 흐름입니다.
1. API 정상 동작 처리
- 클라이언트는 데이터를 담아서 @RequestBody, @RequstParam, @PathVariable Annotation을 이용하여 API로 호출을 합니다.
- Controller에서는 데이터를 이에 대한 처리를 수행합니다.
- 데이터 처리가 정상적으로 처리된 경우
- Controller 처리가 완료되면 비즈니스 로직 처리를 수행합니다.
- 비즈니스 로직 처리가 완료되면 Controller에서 클라이언트로 ‘성공 응답(Response)’ 데이터를 전송합니다.
- 데이터 처리가 정상적으로 처리된 경우
- 클라이언트와 API와의 통신이 완료되었습니다.
2. API 비 정상 동작 처리 : Exception 발생 시
- 클라이언트는 데이터를 담아서 @RequestBody, @RequstParam, @PathVariable Annotation을 이용하여 API로 호출을 합니다.
- Controller에서는 데이터를 이에 대한 처리를 수행합니다.
- 데이터 처리가 정상적으로 처리가 되지 않고 Exception이 발생한 경우
- 비즈니스 로직의 처리 없이 @RestControllerAdvice / @ExceptionHandler로 구성한 ‘GlobalException’에서 에러를 캐치합니다.
- @ExceptionHandler(MethodArgumentNotValidException.class)로 에러로 발생시키는 것이 아닌 클라이언트로 ‘에러 응답(Response)’ 데이터를 전송합니다.
- 데이터 처리가 정상적으로 처리가 되지 않고 Exception이 발생한 경우
- 클라이언트와 API와의 통신이 완료되었습니다.
3) Global Exception Handler Annotation
💡 전역 에러 핸들러(Global Exception Handler) 란?
- @ControllerAdvice / @RestControllerAdvice과 @ExceptionHandler 어노테이션을 기반으로 Controller 내에서 발생하는 에러에 대해서 해당 핸들러에서 캐치하여 오류를 발생시키지 않고 응답 메시지로 클라이언트에게 전달해 주는 기능을 의미합니다.
- HTTP Status 코드로 정상코드가 아닌 '오류코드'로 반환하였을 시 실제 '에러'가 발생하기에 이를 위해 중간에 GlobalExcpetion을 통해 Exception 발생 시에도 HTTP Status 코드로 '정상 코드'를 보내고 커스텀한 코드를 보냄으로써 실제 Client 내에서 이를 처리할 수 있게 돕기 위함입니다.
1. @ControllerAdvice
💡@ControllerAdvice
- @Controller로 선언한 지점에서 발생한 에러를 도중에 @ControllerAdivce로 선언한 클래스 내에서 이를 캐치하여 Controller 내에서 발생한 에러를 처리할 수 있도록 하는 어노테이션입니다.
-@ControllerAdivce는 Spring AOP를 이용한 어노테이션입니다.
[ 더 알아보기 ]
💡 Spring AOP(Aspect-Oriented-Programming) : 관점 지향 프로그래밍
- 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나누어서 그 관점을 기준으로 모듈화를 수행하는 의미입니다.
- 다시 말해, 비즈니스 함수가 실행되는 전후 특정 동작이 수행되는 시점에 특정 모듈을 수행시키는데 활용하는 기능입니다.
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
System.out.println("MethodArgumentNotValidException 에러를 캐치");
}
}
[참고] Spring Boot AOP에 대해서 궁금하시다면 아래의 링크를 참고하면 도움이 많이 됩니다.
2. @RestControllerAdvice
💡@RestControllerAdvice
- @RestController로 선언한 지점에서 발생한 에러를 도중에 @RestControllerAdvice로 선언한 클래스 내에서 이를 캐치하여 Controller 내에서 발생한 에러를 처리할 수 있도록 하는 어노테이션입니다.
- @ControllerAdivce는 Spring AOP를 이용한 어노테이션입니다.
[ 더 알아보기 ]
💡 @ControllerAdvice vs @RestControllerAdvice 차이점
- @ControllerAdvice와 다른점은 @RequestBody 어노테이션이 포함되어 있어서 응답(Response)을 JSON으로 제공을 해준다는 차이점이 있습니다.
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
System.out.println("MethodArgumentNotValidException 에러를 캐치");
}
}
3. @ExceptionHandler
💡 @ExceptionHandler
- 특정 에러 발생 시 Controller에 발생하였을 경우 해당 에러를 캐치하여 클라이언트로 오류를 반환하도록 처리하는 기능을 수행합니다.
/**
* [Exception] Controller에서 @valid 유효성 검증을 통과하지 못 하였을 경우 발생
*
* @param ex MethodArgumentNotValidException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
log.error("handleMethodArgumentNotValidException", ex);
BindingResult bindingResult = ex.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
stringBuilder.append(fieldError.getField()).append(":");
stringBuilder.append(fieldError.getDefaultMessage());
stringBuilder.append(", ");
}
final ErrorResponse response = ErrorResponse.of(ErrorCode.NOT_VALID_ERROR, String.valueOf(stringBuilder));
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
4) Global Exception Handler 환경설정
💡 해당 환경설정 부분은 Controller 내에서 에러가 발생하였을 때 실제 에러를 발생시키지 않고 반환 값을 클라이언트로 전달하여 에러가 발생한 위치와 종류에 대해서 추적을 위한 목적으로 해당 과정을 환경 설정합니다.
1. 성공 응답 코드 구성
💡 SuccessCode.java 파일로 이름을 명명하여 '성공에 대한 코드'를 관리하기 위한 목적으로 구성합니다.
import lombok.Getter;
/**
* [공통 코드] API 통신에 대한 '에러 코드'를 Enum 형태로 관리를 한다.
* Success CodeList : 성공 코드를 관리한다.
* Success Code Constructor: 성공 코드를 사용하기 위한 생성자를 구성한다.
*
* @author lee
*/
@Getter
public enum SuccessCode {
/**
* ******************************* Success CodeList ***************************************
*/
// 조회 성공 코드 (HTTP Response: 200 OK)
SELECT_SUCCESS(200, "200", "SELECT SUCCESS"),
// 삭제 성공 코드 (HTTP Response: 200 OK)
DELETE_SUCCESS(200, "200", "DELETE SUCCESS"),
// 삽입 성공 코드 (HTTP Response: 201 Created)
INSERT_SUCCESS(201, "201", "INSERT SUCCESS"),
// 수정 성공 코드 (HTTP Response: 201 Created)
UPDATE_SUCCESS(204, "204", "UPDATE SUCCESS"),
; // End
/**
* ******************************* Success Code Constructor ***************************************
*/
// 성공 코드의 '코드 상태'를 반환한다.
private final int status;
// 성공 코드의 '코드 값'을 반환한다.
private final String code;
// 성공 코드의 '코드 메시지'를 반환한다.s
private final String message;
// 생성자 구성
SuccessCode(final int status, final String code, final String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
2. 실패 응답 코드 구성
💡 ErrorCode.java 파일로 이름을 명명하여 '실패에 대한 코드'를 관리하기 위한 목적으로 구성합니다.
import lombok.Getter;
/**
* [공통 코드] API 통신에 대한 '에러 코드'를 Enum 형태로 관리를 한다.
* Global Error CodeList : 전역으로 발생하는 에러코드를 관리한다.
* Custom Error CodeList : 업무 페이지에서 발생하는 에러코드를 관리한다
* Error Code Constructor : 에러코드를 직접적으로 사용하기 위한 생성자를 구성한다.
*
* @author lee
*/
@Getter
public enum ErrorCode {
/**
* ******************************* Global Error CodeList ***************************************
* HTTP Status Code
* 400 : Bad Request
* 401 : Unauthorized
* 403 : Forbidden
* 404 : Not Found
* 500 : Internal Server Error
* *********************************************************************************************
*/
// 잘못된 서버 요청
BAD_REQUEST_ERROR(400, "G001", "Bad Request Exception"),
// @RequestBody 데이터 미 존재
REQUEST_BODY_MISSING_ERROR(400, "G002", "Required request body is missing"),
// 유효하지 않은 타입
INVALID_TYPE_VALUE(400, "G003", " Invalid Type Value"),
// Request Parameter 로 데이터가 전달되지 않을 경우
MISSING_REQUEST_PARAMETER_ERROR(400, "G004", "Missing Servlet RequestParameter Exception"),
// 입력/출력 값이 유효하지 않음
IO_ERROR(400, "G005", "I/O Exception"),
// com.google.gson JSON 파싱 실패
JSON_PARSE_ERROR(400, "G006", "JsonParseException"),
// com.fasterxml.jackson.core Processing Error
JACKSON_PROCESS_ERROR(400, "G007", "com.fasterxml.jackson.core Exception"),
// 권한이 없음
FORBIDDEN_ERROR(403, "G008", "Forbidden Exception"),
// 서버로 요청한 리소스가 존재하지 않음
NOT_FOUND_ERROR(404, "G009", "Not Found Exception"),
// NULL Point Exception 발생
NULL_POINT_ERROR(404, "G010", "Null Point Exception"),
// @RequestBody 및 @RequestParam, @PathVariable 값이 유효하지 않음
NOT_VALID_ERROR(404, "G011", "handle Validation Exception"),
// @RequestBody 및 @RequestParam, @PathVariable 값이 유효하지 않음
NOT_VALID_HEADER_ERROR(404, "G012", "Header에 데이터가 존재하지 않는 경우 "),
// 서버가 처리 할 방법을 모르는 경우 발생
INTERNAL_SERVER_ERROR(500, "G999", "Internal Server Error Exception"),
/**
* ******************************* Custom Error CodeList ***************************************
*/
// Transaction Insert Error
INSERT_ERROR(200, "9999", "Insert Transaction Error Exception"),
// Transaction Update Error
UPDATE_ERROR(200, "9999", "Update Transaction Error Exception"),
// Transaction Delete Error
DELETE_ERROR(200, "9999", "Delete Transaction Error Exception"),
; // End
/**
* ******************************* Error Code Constructor ***************************************
*/
// 에러 코드의 '코드 상태'을 반환한다.
private final int status;
// 에러 코드의 '코드간 구분 값'을 반환한다.
private final String divisionCode;
// 에러 코드의 '코드 메시지'을 반환한다.
private final String message;
// 생성자 구성
ErrorCode(final int status, final String divisionCode, final String message) {
this.status = status;
this.divisionCode = divisionCode;
this.message = message;
}
}
3. API 성공 응답 반환 값 구성
💡 ApiResponse.java 파일로 이름을 명명하여 클라이언트에서 요청한 값에 대해 '성공한 응답' 반환 값의 형태를 구성하였습니다.
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* [공통] API Response 결과의 반환 값을 관리
*/
@Getter
public class ApiResponse<T> {
// API 응답 결과 Response
private T result;
// API 응답 코드 Response
private int resultCode;
// API 응답 코드 Message
private String resultMsg;
@Builder
public ApiResponse(final T result, final int resultCode, final String resultMsg) {
this.result = result;
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
}
4. API 실패 응답 반환 값 구성
💡ErrorResponse.java 파일로 이름을 명명하여 클라이언트에서 요청한 값에 대해 '실패한 응답' 반환 값의 형태를 구성하였습니다.
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.validation.BindingResult;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Global Exception Handler에서 발생한 에러에 대한 응답 처리를 관리
*/
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ErrorResponse {
private int status; // 에러 상태 코드
private String divisionCode; // 에러 구분 코드
private String resultMsg; // 에러 메시지
private List<FieldError> errors; // 상세 에러 메시지
private String reason; // 에러 이유
/**
* ErrorResponse 생성자-1
*
* @param code ErrorCode
*/
@Builder
protected ErrorResponse(final ErrorCode code) {
this.resultMsg = code.getMessage();
this.status = code.getStatus();
this.divisionCode = code.getDivisionCode();
this.errors = new ArrayList<>();
}
/**
* ErrorResponse 생성자-2
*
* @param code ErrorCode
* @param reason String
*/
@Builder
protected ErrorResponse(final ErrorCode code, final String reason) {
this.resultMsg = code.getMessage();
this.status = code.getStatus();
this.divisionCode = code.getDivisionCode();
this.reason = reason;
}
/**
* ErrorResponse 생성자-3
*
* @param code ErrorCode
* @param errors List<FieldError>
*/
@Builder
protected ErrorResponse(final ErrorCode code, final List<FieldError> errors) {
this.resultMsg = code.getMessage();
this.status = code.getStatus();
this.errors = errors;
this.divisionCode = code.getDivisionCode();
}
/**
* Global Exception 전송 타입-1
*
* @param code ErrorCode
* @param bindingResult BindingResult
* @return ErrorResponse
*/
public static ErrorResponse of(final ErrorCode code, final BindingResult bindingResult) {
return new ErrorResponse(code, FieldError.of(bindingResult));
}
/**
* Global Exception 전송 타입-2
*
* @param code ErrorCode
* @return ErrorResponse
*/
public static ErrorResponse of(final ErrorCode code) {
return new ErrorResponse(code);
}
/**
* Global Exception 전송 타입-3
*
* @param code ErrorCode
* @param reason String
* @return ErrorResponse
*/
public static ErrorResponse of(final ErrorCode code, final String reason) {
return new ErrorResponse(code, reason);
}
/**
* 에러를 e.getBindingResult() 형태로 전달 받는 경우 해당 내용을 상세 내용으로 변경하는 기능을 수행한다.
*/
@Getter
public static class FieldError {
private final String field;
private final String value;
private final String reason;
public static List<FieldError> of(final String field, final String value, final String reason) {
List<FieldError> fieldErrors = new ArrayList<>();
fieldErrors.add(new FieldError(field, value, reason));
return fieldErrors;
}
private static List<FieldError> of(final BindingResult bindingResult) {
final List<org.springframework.validation.FieldError> fieldErrors = bindingResult.getFieldErrors();
return fieldErrors.stream()
.map(error -> new FieldError(
error.getField(),
error.getRejectedValue() == null ? "" : error.getRejectedValue().toString(),
error.getDefaultMessage()))
.collect(Collectors.toList());
}
@Builder
FieldError(String field, String value, String reason) {
this.field = field;
this.value = value;
this.reason = reason;
}
}
}
5. GlobalExceptionHandler 구성
💡 최종적으로 구성한 GlobalExceptionHandler에 대해서 구성하였습니다.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.gson.JsonParseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.io.IOException;
/**
* Controller 내에서 발생하는 Exception 대해서 Catch 하여 응답값(Response)을 보내주는 기능을 수행함.
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
private final HttpStatus HTTP_STATUS_OK = HttpStatus.OK;
/**
* [Exception] API 호출 시 '객체' 혹은 '파라미터' 데이터 값이 유효하지 않은 경우
*
* @param ex MethodArgumentNotValidException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
log.error("handleMethodArgumentNotValidException", ex);
BindingResult bindingResult = ex.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
stringBuilder.append(fieldError.getField()).append(":");
stringBuilder.append(fieldError.getDefaultMessage());
stringBuilder.append(", ");
}
final ErrorResponse response = ErrorResponse.of(ErrorCode.NOT_VALID_ERROR, String.valueOf(stringBuilder));
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* [Exception] API 호출 시 'Header' 내에 데이터 값이 유효하지 않은 경우
*
* @param ex MissingRequestHeaderException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(MissingRequestHeaderException.class)
protected ResponseEntity<ErrorResponse> handleMissingRequestHeaderException(MissingRequestHeaderException ex) {
log.error("MissingRequestHeaderException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.REQUEST_BODY_MISSING_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* [Exception] 클라이언트에서 Body로 '객체' 데이터가 넘어오지 않았을 경우
*
* @param ex HttpMessageNotReadableException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
protected ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException(
HttpMessageNotReadableException ex) {
log.error("HttpMessageNotReadableException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.REQUEST_BODY_MISSING_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* [Exception] 클라이언트에서 request로 '파라미터로' 데이터가 넘어오지 않았을 경우
*
* @param ex MissingServletRequestParameterException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
protected ResponseEntity<ErrorResponse> handleMissingRequestHeaderExceptionException(
MissingServletRequestParameterException ex) {
log.error("handleMissingServletRequestParameterException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.MISSING_REQUEST_PARAMETER_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
/**
* [Exception] 잘못된 서버 요청일 경우 발생한 경우
*
* @param e HttpClientErrorException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(HttpClientErrorException.BadRequest.class)
protected ResponseEntity<ErrorResponse> handleBadRequestException(HttpClientErrorException e) {
log.error("HttpClientErrorException.BadRequest", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.BAD_REQUEST_ERROR, e.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* [Exception] 잘못된 주소로 요청 한 경우
*
* @param e NoHandlerFoundException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(NoHandlerFoundException.class)
protected ResponseEntity<ErrorResponse> handleNoHandlerFoundExceptionException(NoHandlerFoundException e) {
log.error("handleNoHandlerFoundExceptionException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.NOT_FOUND_ERROR, e.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* [Exception] NULL 값이 발생한 경우
*
* @param e NullPointerException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(NullPointerException.class)
protected ResponseEntity<ErrorResponse> handleNullPointerException(NullPointerException e) {
log.error("handleNullPointerException", e);
final ErrorResponse response = ErrorResponse.of(ErrorCode.NULL_POINT_ERROR, e.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* Input / Output 내에서 발생한 경우
*
* @param ex IOException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(IOException.class)
protected ResponseEntity<ErrorResponse> handleIOException(IOException ex) {
log.error("handleIOException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.IO_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* com.google.gson 내에 Exception 발생하는 경우
*
* @param ex JsonParseException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(JsonParseException.class)
protected ResponseEntity<ErrorResponse> handleJsonParseExceptionException(JsonParseException ex) {
log.error("handleJsonParseExceptionException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.JSON_PARSE_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
/**
* com.fasterxml.jackson.core 내에 Exception 발생하는 경우
*
* @param ex JsonProcessingException
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(JsonProcessingException.class)
protected ResponseEntity<ErrorResponse> handleJsonProcessingException(JsonProcessingException ex) {
log.error("handleJsonProcessingException", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.REQUEST_BODY_MISSING_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
// ==================================================================================================================
/**
* [Exception] 모든 Exception 경우 발생
*
* @param ex Exception
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(Exception.class)
protected final ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
log.error("Exception", ex);
final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR, ex.getMessage());
return new ResponseEntity<>(response, HTTP_STATUS_OK);
}
}
6. 최종 구성한 패키지 형태
5) 구성한 환경에 따라 테스트
1. VO 환경에서 Validation으로 제약을 걸어둡니다.
💡 Spring Validation을 이용하여 VO내에 제약조건을 걸어두었습니다.
package com.adjh.multiflexapi.model;
import lombok.*;
import javax.validation.constraints.*;
import java.sql.Timestamp;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CodeVO {
@NotBlank(message = "grpCd is mandatory")
@Size(min = 1, max = 16, message = "grpCd must be between 1 and 16")
private String grpCd;
@NotBlank
@Size(min = 1, max = 16)
private String cd;
@NotBlank
@Size(min = 1, max = 50)
private String grpCdNm;
@NotBlank
@Size(min = 1, max = 50)
private String cdNm;
@Min(1)
private int sortOrder;
private Timestamp regDt;
private boolean useYn;
@Builder
public CodeVO(String grpCd, String cd, String grpCdNm, String cdNm, int sortOrder, Timestamp regDt, boolean useYn) {
this.grpCd = grpCd;
this.cd = cd;
this.grpCdNm = grpCdNm;
this.cdNm = cdNm;
this.sortOrder = sortOrder;
this.regDt = regDt;
this.useYn = useYn;
}
}
2. Controller를 구성합니다.
@Slf4j
@RestController
@RequestMapping(value = "/api/v1/code")
public class CodeController {
/**
* [API] 코드 등록
*
* @return ResponseEntity<Integer> : 응답 결과 및 응답 코드 반환
*/
@PostMapping("/insertCode")
public ResponseEntity<Integer> insertCode(@RequestBody @Valid CodeVO codeVO) {
log.debug("코드를 등록합니다.");
Integer resultList = codeService.insertCode(codeVO);
return new ResponseEntity<>(resultList, HttpStatus.OK);
}
}
3. Client에서 Controller로 에러를 발생시킵니다.
💡 구성한 ErrorCode 형태로 응답 값을 반환받음을 확인하였습니다.
6) 참고
💡 [참고] 비즈니스 로직 단위에서의 Exception 설정에 대해서 궁금하시면 하단의 글을 참고하시면 도움이 많이 됩니다.
💡 [참고] 해당 내용을 담은 Repository입니다
오늘도 감사합니다 😀
반응형
'Java > Spring Boot' 카테고리의 다른 글
[Java] Spring Boot Security 이해하기 -1 : 2.7.x 버전 구조 및 파일 이해 (0) | 2022.12.18 |
---|---|
[Java] Business Exception 이해하고 구성하기 : Service Exception (0) | 2022.12.10 |
[Java/Library] Spring Boot Validation 이해하기 : 데이터 유효성 검증 (4) | 2022.11.08 |
[Java/Library] Thymeleaf, Thymeleaf Layout 적용하기 (6) | 2022.10.30 |
[Java/Library] Slf4j - Log4j2 이해하고 설정하기 (2) | 2022.10.21 |