220716_TIL(3) : 스프링 예외 처리 / Global 예외 처리 방법

백승한·2022년 7월 16일
0

HTTP 상태 코드 종류를 잘 알아둬야할 필요가 있는데,
만약 모니터링을 하는 시스템을 만들었다 라고 하면, 서버가 돌아가면서 문제가 있는 상황에 알림을 주는 설정을 만들어 놓는 경우가 많이 있기 때문이다.

스프링 예외 처리

스프링 예외 처리 방법

  • 현재상황
    - 클라이언트가 '중복되는 폴더명'을 작성해서 에러가 발생하는 상황. 클라이언트 잘못이기 때문에 4XX 오류가 나와야하는데 500 에러가 발생한다.

  • To Do

    1. HTTP 500 → 400 Client Error
    2. 응답 본문(Body) 내용 정의
      • 에러 시 전달되는 본문 정의 → 프론트엔드 개발자와 공유
        1. errorMessage: 에러 내용
        2. httpStatus: 스프링에 선언된 HttpStaus 값
          • org.springframework.http > HttpStatus
      • Sample
        {
          "errorMessage":"중복된 폴더명을 제거해 주세요! 폴더명: 신발",
        	"httpStatus":"BAD_REQUEST"
        }
  • 예외 처리 전

@PostMapping("api/folders")
public List<Folder> addFolders(
        @RequestBody FolderRequestDto folderRequestDto,
        @AuthenticationPrincipal UserDetailsImpl userDetails
) {
    List<String> folderNames = folderRequestDto.getFolderNames();
    User user = userDetails.getUser();

    return folderService.addFolders(folderNames, user);
}
  • 예외 처리 후
@PostMapping("api/folders")
public ResponseEntity addFolders(
        @RequestBody FolderRequestDto folderRequestDto,
        @AuthenticationPrincipal UserDetailsImpl userDetails
) {
    try {
        List<String> folderNames = folderRequestDto.getFolderNames();
        User user = userDetails.getUser();

        List<Folder> folders = folderService.addFolders(folderNames, user);
        return new ResponseEntity(folders, HttpStatus.OK);

    } catch (IllegalArgumentException ex) {

        RestApiException restApiException = new RestApiException();
        restApiException.setHttpStatus(HttpStatus.BAD_REQUEST);
        restApiException.setErrorMessage(ex.getMessage());
        return new ResponseEntity(
                // HTTP body
                restApiException,
                // HTTP status code
                HttpStatus.BAD_REQUEST);
    }
}
  • 문제점
    • 예외 처리해야할 메소드가 1000개라고 하면 ?
      • ---> 이 때 필요한게 @ExceptionHandler 사용
  • @ExceptionHandler
    • FolderController 범위 내에서 모든 함수에 예외처리 적용 (AOP)
    • 예외 처리 함수
     @ExceptionHandler({IllegalArgumentException.class})
      public ResponseEntity handleException(IllegalArgumentException ex) {
          RestApiException restApiException = new RestApiException();
          restApiException.setHttpStatus(HttpStatus.BAD_REQUEST);
          restApiException.setErrorMessage(ex.getMessage());
          return new ResponseEntity(
                  // HTTP body
                  restApiException,
                  // HTTP status code
                  HttpStatus.BAD_REQUEST
          );
      }

스프링 Global 예외 처리

Global 예외처리 방법

ControllerAdvice가 Controller인 척하면서 모든 try/catch를 처리해준다.
ControllerAdvice는 하나가 아닐 수 있고 여러 개로 처리할 수도 있다.

  • @ControllerAdvice 사용
  • @RestControllerAdvice
    • @ControllerAdvice + @ResponseBody
@RestControllerAdvice   // 모든 Controller에 적용이 된다
public class RestApiExceptionHandler {

    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<Object> handleApiRequestException(IllegalArgumentException ex) {
    
        RestApiException restApiException = new RestApiException();
        restApiException.setHttpStatus(HttpStatus.BAD_REQUEST);
        restApiException.setErrorMessage(ex.getMessage());

        return new ResponseEntity(
                restApiException,
                HttpStatus.BAD_REQUEST
        );
    }
}

Exception이 발생했을 때 JSON 형태로 바디 부분에 해당하는 restApiException을 넘겨준다.

Global한 Exception을 할 때 ErrorCode를 선언해서 사용하는 경우도 많다.

ErrorCode 선언

  • 서비스 전체에 사용할 에러코드들 (ErrorCode) 을 선언

    • 예외발생 시 서버 및 클라이언트에서 선언한 ErrorCode 사용

    • 에러코드 샘플

      import org.springframework.http.HttpStatus;
      
      import static com.sparta.springcore.service.ProductService.MIN_MY_PRICE;
      
      public enum ErrorCode {
          // 400 Bad Request
          DUPLICATED_FOLDER_NAME(HttpStatus.BAD_REQUEST, "400_1", "중복폴더명이 이미 존재합니다."),
          BELOW_MIN_MY_PRICE(HttpStatus.BAD_REQUEST, "400_2", "최저 희망가는 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요."),
      
          // 404 Not Found
          NOT_FOUND_PRODUCT(HttpStatus.NOT_FOUND, "404_1", "해당 관심상품 아이디가 존재하지 않습니다."),
          NOT_FOUND_FOLDER(HttpStatus.NOT_FOUND, "404_2", "해당 폴더 아이디가 존재하지 않습니다."),
          ;
      
          private final HttpStatus httpStatus;
          private final String errorCode;
          private final String errorMessage;
      
          ErrorCode(HttpStatus httpStatus, String errorCode, String errorMessage) {
              this.httpStatus = httpStatus;
              this.errorCode = errorCode;
              this.errorMessage = errorMessage;
          }
      }
      1. httpStatus: HTTP 상태코드
      2. errorCode: 에러 코드
        1. 에러 종류별로 Unique 한 에러코드를 소유
        2. 국제화에 사용 가능
          1. 클라이언트가 사용하는 언어 (한국어, 영어, 중국어 등) 에 따라 에러메시지를 다르게 보여줌
      3. errorMessage
        1. 대표 에러 메시지

기록

전에 한번 본적 있던 예외처리 부분을 제대로 공부하게돼서 관심있게 공부하게 됐다.
다만 ErrorCode 활용 방법은 현업에서 많이 사용하기에 더 찾아봐서 적용해보고 익숙해져야될 필요가 있다고 생각한다.

  • 5주차 강의를 마치면서 남기신 말씀

    • 스프링도 그렇고, 프로그래밍도 그렇고 암기과목이 아니라는 것을 명심해라.

    • 5주동안 다뤘던 내용들을 모두 다 암기하려고 노력하지 말아라.
      한번 성공한 경험, 프로젝트를 돌려본 경험이 있다면, 나중에 구글링을 통해서 찾을 능력이 있다.

    • 암기보다는 원리파악을 해놓으면 검색할 때 더 수월하다.

profile
방문해주셔서 감사합니다🙂

0개의 댓글