ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 에러 메시지 처리 (Custom Exception)
    Spring Boot/기타 2022. 6. 6. 13:07
    반응형

    1. build.grade

    plugins {
        id 'org.springframework.boot' version '2.7.0'
        id 'io.spring.dependency-management' version '1.0.11.RELEASE'
        id 'java'
    }
    
    group = 'com.example'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '11'
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        // spring-boot-starter 의존성 추가
        implementation 'org.springframework.boot:spring-boot-starter-validation'
        implementation 'org.springframework.boot:spring-boot-starter-web'
    
        // commons-lang3 의존성 추가
        implementation 'org.apache.commons:commons-lang3:3.11'
    
        // lombok 의존성 추가
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
    
        // spring-boot-starter-test 의존성 추가
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }
    
    tasks.named('test') {
        useJUnitPlatform()
    }

     

    2. application.yml

    # Spring 설정
    spring:
      web:
        locale: ko_KR
        resources:
          add-mappings: false
      messages:
        basename: message/messages
      # 404(Not Found) 발생 시 throw 하도록 활성화
      mvc:
        throw-exception-if-no-handler-found: true

     

    3. ErrorResponse 구현

    ErrorResponse.java

    package com.example.customexception.response;
    
    import com.example.customexception.constant.ResponseCode;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.Setter;
    import org.apache.commons.lang3.StringUtils;
    
    @Getter
    @Setter
    public class ErrorResponse {
    
      @JsonProperty("status_code")
      private String statusCode;
    
      @JsonProperty("message")
      private String message;
    
      @JsonProperty("method")
      private String method;
    
      @JsonProperty("path")
      private String path;
    
      @JsonProperty("timestamp")
      private String timestamp;
    
      @Builder
      ErrorResponse(
          String statusCode,
          String message,
          String method,
          String path,
          String timestamp
      ) {
        this.statusCode = statusCode;
        this.message =
            StringUtils.isNotBlank(message) ? message
                : ResponseCode.valueOfStatusCode(statusCode).getMessage();
        this.method = method;
        this.path = path;
        this.timestamp =
            StringUtils.isNotBlank(timestamp) ? timestamp
                : LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMDDHHMMSS"));
      }
    }

     

    4. ResponseCode 구현

    ResponseCode.java

    package com.example.customexception.constant;
    
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    
    @AllArgsConstructor
    @Getter
    public enum ResponseCode {
    
      OK("200", "성공"),
    
      // Custom Exception 에러 코드
      CUSTOM_EXCEPTION("800", "오류가 발생했습니다. 확인 후 다시 시도해주세요."),
      INVALID_REQUEST("801", "유효하지 않은 요청입니다."),
      FORBIDDEN_REQUEST("802", "허용되지 않은 요청입니다."),
      DUPLICATED_REQUEST("803", "중복된 요청입니다."),
      NOT_FOUND("804", "존재하지 않는 정보입니다."),
    
      // Exception Handler 에러 코드
      INTERNAL_SERVER_ERROR("900", "내부 오류가 발생했습니다. 확인 후 다시 시도해주세요."),
      METHOD_ARGUMENT_NOT_VALID("901", "파라미터가 유효하지 않습니다."),
      MISSING_SERVLET_REQUEST_PARAMETER("902", "필수 파라미터가 누락되었습니다."),
      CONSTRAINT_VIOLATION("903", "파라미터 유효성 검사에 실패했습니다."),
      METHOD_ARGUMENT_TYPE_MISMATCH("904", "파라미터 타입이 올바르지 않습니다."),
      HTTP_MESSAGE_NOT_READABLE_EXCEPTION("905", "읽을 수 있는 요청 정보가 없습니다."),
      NO_HANDLER_FOUND("906", "요청한 URL을 찾을 수 없습니다."),
      HTTP_REQUEST_METHOD_NOT_SUPPORTED("907", "지원하지 않는 메서드입니다."),
      HTTP_MEDIA_TYPE_NOT_SUPPORTED("908", "지원되지 않는 미디어 타입입니다.");
    
      private final String code;
      private final String message;
    
      private static final Map<String, ResponseCode> BY_STATUS_CODE =
          Stream.of(values())
              .collect(Collectors.toMap(ResponseCode::getCode, Function.identity()));
    
      private static final Map<String, ResponseCode> BY_MESSAGE =
          Stream.of(values())
              .collect(Collectors.toMap(ResponseCode::getMessage, Function.identity()));
    
      public static ResponseCode valueOfStatusCode(String statusCode) {
        return BY_STATUS_CODE.get(statusCode);
      }
    
      public static ResponseCode valueOfMessage(String message) {
        return BY_MESSAGE.get(message);
      }
    }

     

    5. ErrorException 구현

    ErrorException.java

    package com.example.customexception.exception;
    
    import com.example.customexception.constant.ResponseCode;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    public class ErrorException extends RuntimeException {
    
      private ResponseCode responseCode;
      private String message;
    
      public ErrorException(ResponseCode rtaStatusCode) {
        super();
        this.responseCode = rtaStatusCode;
        this.message = rtaStatusCode.getMessage();
      }
    
      public ErrorException(String message) {
        super();
        this.responseCode = ResponseCode.CUSTOM_EXCEPTION;
        this.message = message;
      }
    }


     

    6. ErrorExceptionDto 구현

    ErrorExceptionDto.java

    package com.example.customexception.dto;
    
    import com.fasterxml.jackson.annotation.JsonProperty;
    import javax.validation.constraints.AssertFalse;
    import javax.validation.constraints.NotBlank;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    import lombok.ToString;
    
    public class ErrorExceptionDto {
    
      @Getter
      @Setter
      @Builder
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      public static class Request {
    
        @JsonProperty("id")
        private Long id;
    
        @NotBlank
        @JsonProperty("title")
        private String title;
    
        @NotBlank
        @JsonProperty("description")
        private String description;
    
        @AssertFalse
        @JsonProperty("completed")
        private Boolean completed;
      }
    
      @Getter
      @Setter
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      public class Response {
    
        @JsonProperty("id")
        private Long id;
    
        @JsonProperty("title")
        private String titile;
    
        @JsonProperty("description")
        private String description;
    
        @JsonProperty("completed")
        private Boolean completed;
      }
    }

     

    7. ExceptionAspect 구현

    ExceptionAspect.java

    package com.example.customexception.advice;
    
    import com.example.customexception.constant.ResponseCode;
    import com.example.customexception.exception.ErrorException;
    import com.example.customexception.response.ErrorResponse;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.http.HttpServletRequest;
    import javax.validation.ConstraintViolationException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.util.ObjectUtils;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.FieldError;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.HttpMediaTypeNotSupportedException;
    import org.springframework.web.HttpRequestMethodNotSupportedException;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.MissingServletRequestParameterException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
    import org.springframework.web.servlet.NoHandlerFoundException;
    
    @Slf4j
    @RestControllerAdvice
    public class ExceptionAspect {
    
      /**
       * ExceptionHandler - Exception
       */
      @ExceptionHandler(Exception.class)
      public ResponseEntity<ErrorResponse> handleException(
          HttpServletRequest request,
          Exception e) {
        log.error("handleException : {}", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.INTERNAL_SERVER_ERROR.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - Custom Exception
       */
      @ExceptionHandler(ErrorException.class)
      public ResponseEntity<ErrorResponse> handleRtaException(
          HttpServletRequest request,
          ErrorException e) {
        log.error("handleRtaException : {}", e);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(e.getResponseCode().getCode())
                .method(request.getMethod())
                .message(e.getMessage())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - MethodArgumentNotValidException
       */
      @ExceptionHandler(MethodArgumentNotValidException.class)
      public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(
          HttpServletRequest request,
          MethodArgumentNotValidException e) {
        log.error("handleMethodArgumentNotValidException : {}", e);
    
        String message = "";
    
        List<String> messages = new ArrayList<>();
        BindingResult bindingResult = e.getBindingResult();
        for (ObjectError error : bindingResult.getGlobalErrors()) {
          messages.add(
              camelToSnake(error.getObjectName()) + ":" + error.getDefaultMessage());
        }
    
        for (FieldError error : bindingResult.getFieldErrors()) {
          messages.add(camelToSnake(error.getField()) + ":" + error.getDefaultMessage());
        }
    
        if (!ObjectUtils.isEmpty(messages)) {
          message = String.join(", ", messages);
        }
    
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.METHOD_ARGUMENT_NOT_VALID.getCode())
                .message(message)
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - MissingServletRequestParameterException
       */
      @ExceptionHandler(MissingServletRequestParameterException.class)
      public ResponseEntity<ErrorResponse> handleMissingServletRequestParameterException(
          HttpServletRequest request,
          MissingServletRequestParameterException e) {
        log.error("handleMissingServletRequestParameterException : {}", e);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - ConstraintViolationException
       */
      @ExceptionHandler(ConstraintViolationException.class)
      public ResponseEntity<ErrorResponse> handleConstraintViolationException(
          HttpServletRequest request,
          ConstraintViolationException e) {
        log.error("handleConstraintViolationException : {}", e);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.CONSTRAINT_VIOLATION.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - MethodArgumentTypeMismatchException
       */
      @ExceptionHandler(MethodArgumentTypeMismatchException.class)
      public ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(
          HttpServletRequest request,
          MethodArgumentTypeMismatchException e) {
        log.error("handleMethodArgumentTypeMismatchException : {}", e);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - HttpMessageNotReadableException
       */
      @ExceptionHandler(HttpMessageNotReadableException.class)
      public ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException(
          HttpServletRequest request,
          HttpMessageNotReadableException e) {
        log.error("handleHttpMessageNotReadableException : {}", e);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - NoHandlerFoundException
       */
      @ExceptionHandler(NoHandlerFoundException.class)
      public ResponseEntity<ErrorResponse> handleNoHandlerFoundException(
          HttpServletRequest request,
          NoHandlerFoundException e) {
        log.error("handleNoHandlerFoundException : {}", e);
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.NO_HANDLER_FOUND.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - HttpRequestMethodNotSupportedException
       */
      @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
      public ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(
          HttpServletRequest request,
          HttpRequestMethodNotSupportedException e) {
        log.error("handleHttpRequestMethodNotSupportedException : {}", e);
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * ExceptionHandler - HttpMediaTypeNotSupportedException
       */
      @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
      public ResponseEntity<ErrorResponse> handleHttpMediaTypeNotSupportedException(
          HttpServletRequest request,
          HttpMediaTypeNotSupportedException e) {
        log.error("handleHttpMediaTypeNotSupportedException : {}", e);
        return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).body(
            ErrorResponse.builder()
                .statusCode(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getCode())
                .method(request.getMethod())
                .path(request.getRequestURI())
                .build()
        );
      }
    
      /**
       * Camal Case를 Snake Case로 변환
       */
      public static String camelToSnake(String str) {
    
        StringBuilder sb = new StringBuilder();
    
        char c = str.charAt(0);
        sb.append(Character.toLowerCase(c));
    
        for (int i = 1; i < str.length(); i++) {
          char ch = str.charAt(i);
          if (Character.isUpperCase(ch)) {
            sb.append('_');
          }
          sb.append(Character.toLowerCase(ch));
        }
    
        return sb.toString();
      }
    }

     

    8. ExceptionController 구현

    ExceptionController.java

    package com.example.customexception.controller;
    
    import com.example.customexception.constant.ResponseCode;
    import com.example.customexception.dto.ErrorExceptionDto;
    import com.example.customexception.exception.ErrorException;
    import java.util.Set;
    import javax.validation.ConstraintViolation;
    import javax.validation.ConstraintViolationException;
    import javax.validation.Valid;
    import javax.validation.Validation;
    import javax.validation.Validator;
    import org.springframework.web.bind.annotation.DeleteMapping;
    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.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class ExceptionController {
    
      /**
       * RuntimeException
       */
      @GetMapping("/api/exception01")
      public String exception01() {
        throw new RuntimeException("RuntimeException 발생");
      }
    
      /**
       * ErrorException(ResponseCode rtaStatusCode)
       */
      @GetMapping("/api/exception02")
      public String exception02() {
        throw new ErrorException(ResponseCode.CUSTOM_EXCEPTION);
      }
    
      /**
       * ErrorException(String message)
       */
      @GetMapping("/api/exception03")
      public String exception03() {
        throw new ErrorException("ErrorException 발생");
      }
    
      /**
       * MethodArgumentNotvalidException
       */
      @PostMapping("/api/methodArgumentNotvalidException")
      public String methodArgumentNotValidException(
          @Valid @RequestBody ErrorExceptionDto.Request errorRequest
      ) {
        return "methodArgumentNotvalidException";
      }
    
      /**
       * MethodArgumentNotvalidException
       */
      @GetMapping("/api/missingServletRequestParameterException")
      public String missingServletRequestParameterException(
          @RequestParam("id") Long id
      ) {
        return "missingServletRequestParameterException";
      }
    
      /**
       * MethodArgumentNotvalidException
       */
      @PostMapping("/api/constraintViolationException")
      public String constraintViolationException(
          @RequestBody ErrorExceptionDto.Request errorRequest
      ) {
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<ErrorExceptionDto.Request>> violations
            = validator.validate(errorRequest);
        if (!violations.isEmpty()) {
          throw new ConstraintViolationException(violations);
        }
        return "constraintViolationException";
      }
    
      /**
       * MethodArgumentNotvalidException
       */
      @GetMapping("/api/methodArgumentTypeMismatchException")
      public String methodArgumentTypeMismatchException(
          @RequestParam("id") Long id
      ) {
        return "methodArgumentTypeMismatchException";
      }
    
      /**
       * HttpMessageNotReadableException
       */
      @PostMapping("/api/httpMessageNotReadableException")
      public String httpMessageNotReadableException(
          @Valid @RequestBody ErrorExceptionDto.Request errorRequest
      ) {
        return "httpMessageNotReadableException";
      }
    
      /**
       * NoHandlerFoundException
       */
      @DeleteMapping("/api/noHandlerFoundException")
      public String noHandlerFoundException(
          @RequestParam("id") Long id
      ) {
        return "noHandlerFoundException";
      }
    
      /**
       * HttpRequestMethodNotSupportedException
       */
      @PostMapping("/api/httpRequestMethodNotSupportedException")
      public String httpRequestMethodNotSupportedException(
          @Valid @RequestBody ErrorExceptionDto.Request errorRequest
      ) {
        return "httpRequestMethodNotSupportedException";
      }
    
      /**
       * HttpMediaTypeNotSupportedException
       */
      @PostMapping("/api/httpMediaTypeNotSupportedException")
      public String httpMediaTypeNotSupportedException(
          @Valid @RequestBody ErrorExceptionDto.Request errorRequest
      ) {
        return "httpMediaTypeNotSupportedException";
      }
    }

     

    9. ExceptionControllerTest 구현

    ExceptionControllerTest.java

    package com.example.customexception.controller;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    import com.example.customexception.constant.ResponseCode;
    import com.example.customexception.dto.ErrorExceptionDto;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.junit.jupiter.api.Disabled;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.MethodOrderer;
    import org.junit.jupiter.api.Order;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.TestMethodOrder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.ResultActions;
    import org.springframework.util.LinkedMultiValueMap;
    
    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    @AutoConfigureMockMvc
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @Disabled
    class ExceptionControllerTest {
    
      @Autowired
      MockMvc mockMvc;
    
      @Autowired
      ObjectMapper objectMapper;
    
      @Order(1)
      @DisplayName("exception01_조회_에러 코드:900")
      @Test
      void testException01() throws Exception {
    
        // Given & When
        String url = "/api/exception01";
        ResultActions resultActions = mockMvc.perform(get(url));
    
        // Then
        resultActions
            .andExpect(status().is5xxServerError())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.INTERNAL_SERVER_ERROR.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.INTERNAL_SERVER_ERROR.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(2)
      @DisplayName("exception02_조회_에러 코드:800")
      @Test
      void testException02() throws Exception {
    
        // Given & When
        String url = "/api/exception02";
        ResultActions resultActions = mockMvc.perform(get(url));
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.CUSTOM_EXCEPTION.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.CUSTOM_EXCEPTION.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(3)
      @DisplayName("exception03_조회_에러 코드:800")
      @Test
      void testException03() throws Exception {
    
        // Given & When
        String url = "/api/exception03";
        ResultActions resultActions = mockMvc.perform(get(url));
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.CUSTOM_EXCEPTION.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value("ErrorException 발생")
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(4)
      @DisplayName("methodArgumentNotValidException_조회_에러 코드:901")
      @Test
      void testMethodArgumentNotValidException() throws Exception {
    
        // Given & When
        String url = "/api/methodArgumentNotvalidException";
        ErrorExceptionDto.Request errorRequest = ErrorExceptionDto.Request.builder()
            .title("")
            .description("")
            .completed(false)
            .build();
    
        ResultActions resultActions = mockMvc.perform(
            post(url)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(errorRequest))
        );
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.METHOD_ARGUMENT_NOT_VALID.getCode())
            )
            .andExpect(jsonPath("$.message").isNotEmpty())
            .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(5)
      @DisplayName("missingServletRequestParameterException_조회_에러 코드:902")
      @Test
      void testMissingServletRequestParameterException() throws Exception {
    
        // Given & When
        String url = "/api/missingServletRequestParameterException";
        LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>();
        queryParams.add("missingId", "1");
    
        ResultActions resultActions = mockMvc.perform(
            get(url)
                .queryParams(queryParams)
        );
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(6)
      @DisplayName("constraintViolationException 코드:903")
      @Test
      void testConstraintViolationException() throws Exception {
    
        // Given & When
        String url = "/api/constraintViolationException";
        ErrorExceptionDto.Request errorExceptionDto = ErrorExceptionDto.Request.builder()
            .title("")
            .description("")
            .completed(false)
            .build();
    
        ResultActions resultActions = mockMvc.perform(
            post(url)
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(errorExceptionDto))
        );
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.CONSTRAINT_VIOLATION.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.CONSTRAINT_VIOLATION.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(7)
      @DisplayName("methodArgumentTypeMismatchException_조회_에러 코드:904")
      @Test
      void testMethodArgumentTypeMismatchException() throws Exception {
    
        // Given & When
        String url = "/api/methodArgumentTypeMismatchException";
        LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>();
        queryParams.add("id", "test");
    
        ResultActions resultActions = mockMvc.perform(
            get(url)
                .queryParams(queryParams)
        );
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(8)
      @DisplayName("httpMessageNotReadableException 코드:905")
      @Test
      void testHttpMessageNotReadableException() throws Exception {
    
        // Given & When
        String url = "/api/httpMessageNotReadableException";
        ResultActions resultActions = mockMvc.perform(
            post(url)
                .contentType(MediaType.APPLICATION_JSON)
        );
    
        // Then
        resultActions
            .andExpect(status().isBadRequest())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(9)
      @DisplayName("noHandlerFoundException 코드:906")
      @Test
      void testNoHandlerFoundException() throws Exception {
    
        // Given & When
        String url = "/api/noHandlerFoundExceptionFailed";
        LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>();
        queryParams.add("id", "1");
    
        ResultActions resultActions = mockMvc.perform(
            get(url)
                .queryParams(queryParams)
        );
    
        // Then
        resultActions
            .andExpect(status().isNotFound())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.NO_HANDLER_FOUND.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.NO_HANDLER_FOUND.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(10)
      @DisplayName("httpRequestMethodNotSupportedException 코드:907")
      @Test
      void testHttpRequestMethodNotSupportedException() throws Exception {
    
        // Given & When
        String url = "/api/httpRequestMethodNotSupportedException";
        LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>();
        queryParams.add("id", "1");
    
        ResultActions resultActions = mockMvc.perform(
            get(url)
                .queryParams(queryParams)
        );
    
        // Then
        resultActions
            .andExpect(status().isMethodNotAllowed())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    
      @Order(11)
      @DisplayName("httpMediaTypeNotSupportedException 코드:908")
      @Test
      void testHttpMediaTypeNotSupportedException() throws Exception {
    
        // Given & When
        String url = "/api/httpMediaTypeNotSupportedException";
        ErrorExceptionDto.Request errorExceptionDto = ErrorExceptionDto.Request.builder()
            .title("Spring Boot")
            .description("description00")
            .completed(false)
            .build();
    
        ResultActions resultActions = mockMvc.perform(
            post(url)
                .contentType(MediaType.TEXT_PLAIN)
                .content(objectMapper.writeValueAsString(errorExceptionDto))
        );
    
        // Then
        resultActions
            .andExpect(status().isUnsupportedMediaType())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON))
            .andExpect(
                jsonPath("$.status_code")
                    .value(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getCode())
            )
            .andExpect(
                jsonPath("$.message")
                    .value(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getMessage())
            )
            .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString()))
            .andExpect(jsonPath("$.path").value(url))
            .andExpect(jsonPath("$.timestamp").isNotEmpty())
            .andDo(print());
      }
    }

     

    소스 코드는 Github Repository - https://github.com/tychejin1218/custom-exception 를 참조하세요.

     

    참고 : https://www.baeldung.com/global-error-handler-in-a-spring-rest-api

     

    반응형

    댓글

Designed by Tistory.