이전 글

2022.08.30 - [JAVA/스프링(Spring)] - [스프링] GlobalException 처리 방법 및 예제 정리[1](@ControllerAdvice, @ExceptionHandler, @RestControllerAdvice)

 

[스프링] GlobalException 처리 방법 및 예제 정리[1](@ControllerAdvice, @ExceptionHandler, @RestControllerAdvice)

GlobalException 단어 그대로 전역으로 Exception을 관리한다는 의미를 포함하고 있다. 전역으로 Exception을 관리하기 위해 @ExceptionHandler를 사용법을 정리했습니다. GlobalException 처리를 하는 이유 케이..

whitewise95.tistory.com

 

 

GlobalException

지난 글에는 (@ControllerAdvice, @ExceptionHandler, @RestControllerAdvice) 에 대해 정리 했는데,
이번 글은 (@ControllerAdvice, @ExceptionHandler, @RestControllerAdvice)를
CustomException과  ErrorCode 클래스를 만들어 활요하는 내용을 정리 합니다.

 


RuntimeException extends 하기

아래 코드에 보면 각 생성자와 변수의 사용처를 주석으로 남겨놓았습니다. 충분히 이해가 되실거라 생각이 듭니다.

@Getter
@NoArgsConstructor
public class CustomException extends RuntimeException {

    private String message;  // 프론트에 전달할 message
    private int statusCode;  // 프론트에 전달할 구분할  statusCode
   
    @JsonIgnore  
    private int realStatusCode;   // 실제 발생하는 StatusCode  프론트엔 주지 않을 것으므로 @JsonIgnore 설정

    /*
    *  throw new CustomException(ErrorCode.xxx) 하기 위한 생성자  [1]
    * */
    public CustomException(ErrorCode errorCode) {
        this.message = errorCode.getMessage();
        this.statusCode = errorCode.getStatusCode();
        this.realStatusCode = errorCode.getRealStatusCode();
    }

    /*
     *  @ExceptionHandler 를 위한 생성자   [2]           
     * */
    public CustomException(String message, int statusCode) {
        this.message = message;
        this.statusCode = statusCode;
    }
}

 

 

Enum으로 에러코드 관리하기

아래 코드는 enum으로 에러코드를 관리는 클래스 입니다.  상수명(statusCode, realStatusCode, message) 입니다.
맨 아래 보면 변수명이 CustomException클래스의 변수명과 같은 것을 확인 할 수 있습니다.

@Getter
@RequiredArgsConstructor
public enum ErrorCode {

    //찾을 수 없습니다.
    NOT_FIND_USER(4040, 500, "유저를 찾을 수 없습니다."),

    //형식이 다르거나 유효성 검사
    NOT_MATCH_PASSWORD(5000, 500, "비밀번호가 맞지 않습니다."),

    //사용할 수 없습니다.
    CNT_NOT_USE_NAME(4003, 500, "이용할 수 없는 이름입니다."),
    CNT_NOT_USERNAME(4003, 500, "사용할 수 없는 아이디입니다."),
    NOT_ROLE(4003, 500, "승인되지 않은 회원입니다."),

    //엑세스 토큰
    NOT_HEADER_ACCESS_TOKEN(402, 402, "헤더에 엑세스 토큰이 없습니다."),
    EXPIRATION_ACCESS_TOKEN(410, 410, "기간이 만료된 엑세스 토큰입니다."),
    NOT_ACCESS_TOKEN(411, 411, "변조되었거나 엑세스토큰이 아닙니다."),

    //리프레쉬토큰
    NOT_HEADER_REFRESH_TOKEN(502, 502, "헤더에 리프레쉬 토큰이 없습니다."),
    EXPIRATION_REFRESH_TOKEN(510, 510, "기간이 만료된 리프레쉬 토큰입니다."),
    NOT_REFRESH_TOKEN(511, 511, "변조되어거나 리프레쉬 토큰이 아닙니다.");

    private final int statusCode;
    private final int realStatusCode;
    private final String message;
}

 

 

GlobalException에 @ExceptionHandler 사용하기

이전 글에서 만들었던 GlobalException에 @ExceptionHandler로 위에서 만든 CustomException를 설정해준다.
두번째 주석에 보면 위에 생성자 [2] 에 해당되는 생성자를 사용한다.  

 HttpStatus.valueOf(e.getRealStatusCode()) 이 부분이  실제로 500에러를 낼지 200으로 처리할 지를 결정할 수 있다. 

@RestControllerAdvice
public class GlobalException {

    // 내가 만든 CustomException 클래명
    @ExceptionHandler(CustomException.class)   
    public ResponseEntity<CustomException> customException(CustomException e) {
        
        //생성자 [2]에 해당
        CustomException response = new CustomException(e.getMessage(), e.getStatusCode());

        //realStatusCode에 해당되는 인자
        return new ResponseEntity<>(response, HttpStatus.valueOf(e.getRealStatusCode()));
    }
}

 

 

Throw new CustomException() 사용하기

enum으로 관리하는 ErrorCode 클래스의 상수중 NOT_MATCH_PASSWORD를 직접 이용할 것입니다.

public enum ErrorCode {

    //형식이 다르거나 유효성 검사
    NOT_MATCH_PASSWORD(5000, 500, "제목 글자수를 확인해주세요"),
    
    ....
    ....
}

 

메소드의 일부분이며  직접 예외처리한  throw new CustomException() 에  NOT_MATCH_PASSWORD를 인자로 주면 완료 입니다.

//허가 되지 않은 회원일 때
if (!Optional.ofNullable(user.getRole()).isPresent()) {

	// 생성자[1] 에 해당되는 생성자를 사용하며  ErrorCode에 열거형으로 정의한 상수를 사용
    throw new CustomException(NOT_MATCH_PASSWORD);  
}

 

 

 

Test 해보기

간단한 테스트 입니다. 아래 상수로 TEST와 TEST2 만들어주었고 프론트에  전달할 내용은
첫번째 테스트는 4040에 TEST라는 메세지입니다. 그리고 실제로 StatusCode는 505로 해보겠습니다.
두번째 테스트는 55500에 TEST2라는 메세지와 실제로 StatusCode는 500 입니다.

@Getter
@RequiredArgsConstructor
public enum ErrorCode {
    TEST1(4040, 505, "TEST"),
    TEST2(4040, 505, "TEST"),
    
    private final int statusCode;
    private final int realStatusCode;
    private final String message;
}

 

 

테스트 1

요청 : HTTP.GET /test1  
기대값 : 505에러로 TEST라는 메세지와 4040라는  statusCode를 볼 수 있어야한다.

@GetMapping("/test1")
public void test() {
    throw new CustomException(TEST1);
}

 

실제값  일치

 

 


 

테스트 2

요청 : HTTP.GET /test2 
기대값 : 500에러로 TEST2 라는 메세지와 555000라는 statusCode를 볼 수 있어야한다.

@GetMapping("/test2")
public void test() {
    throw new CustomException(TEST2);
}

 

 

실제값  일치

복사했습니다!