본문 바로가기
spring

[Spring] RuntimeException을 통한 CustomException

by goblin- 2024. 10. 31.

RuntimeException은 Java에서 런타임 시 발생할 수 있는 예외를 처리하는 기본 클래스입니다. RuntimeException은 unchecked 예외로, 이를 상속한 예외는 컴파일 시 예외 처리를 강제하지 않으며, 주로 개발자의 실수나 예측하지 못한 상태에서 발생하는 예외를 다룹니다. RuntimeException을 상속하여 커스텀 예외를 만들면 특정 오류 상황을 더 구체적으로 다룰 수 있고, 개발자에게 명확한 예외 정보를 제공합니다.

 

CustomException 클래스와 ExceptionType 열거형을 통한 RuntimeException의 커스터마이징

 

주어진 예시에서는 CustomExceptionExceptionType 열거형을 정의하여 다양한 유형의 예외를 하나의 커스텀 예외 클래스로 관리하고, 예외에 대한 구체적인 설명과 로그 수준 등을 함께 제공합니다.

 

1. ExceptionType 열거형

 

목적: 예외의 종류를 한 곳에서 정의하여 코드의 가독성과 유지 보수성을 높입니다. 각 예외 유형은 고유의 코드, 설명 메시지, HTTP 상태 코드, 로그 수준을 포함하고 있습니다.

구성요소:

type: 예외의 유형을 나타내는 코드입니다 (예: “A04”).

desc: 예외의 설명으로, 상세 메시지로 사용됩니다.

HttpStatus status: HTTP 응답 상태로 사용될 HTTP 상태 코드를 나타냅니다.

Level level: 예외의 심각도에 따라 로그에 기록할 때의 수준을 나타냅니다.

@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Getter
@RequiredArgsConstructor
public enum ExceptionType {
    USER_NOT_FOUND("A04", "유저가 데이터베이스 내 존재하지 않습니다. 유저 id : ", HttpStatus.NOT_FOUND, Level.WARN),
    PAYMENT_NOT_FOUND("A05", "결제 데이터가 데이터베이스 내 존재하지 않습니다. 결제 id: ", HttpStatus.NOT_FOUND, Level.WARN),
    STATUS_NOT_FOUND("A06", "상태가 데이터베이스 내 존재하지 않습니다. 상태 id :", HttpStatus.NOT_FOUND, Level.WARN),
    INVALID_INPUT("B01", "값을 제대로 주세요 : ", HttpStatus.BAD_REQUEST, Level.WARN),
    UNCLASSIFIED_ERROR("Z10", "내부에서 에러가 발생했습니다. 추가 메세지 : ", HttpStatus.INTERNAL_SERVER_ERROR, Level.ERROR);

    String type;
    String desc;
    HttpStatus status;
    Level level;
}

이 예외 유형을 사용하여 예외 처리 시 코드와 메시지를 보다 명확하게 전달할 수 있습니다.

 

2. CustomException 클래스

 

목적: 예외가 발생했을 때 ExceptionType을 통해 세부 정보(설명, 코드, 상태)를 포함한 커스텀 예외 객체를 생성하여, 코드의 가독성과 예외 추적성을 높입니다.

구성요소:

ExceptionType type: 발생한 예외의 유형을 지정하여 각 유형별 정보를 추가합니다.

CustomException(ExceptionType type, Object message): 예외가 발생했을 때 ExceptionType과 함께 추가 메시지를 전달하여 예외를 생성합니다.

@Getter
public class CustomException extends RuntimeException {
    private ExceptionType type;

    public CustomException(ExceptionType type) {
        super(type.getDesc());
        this.type = type;
    }

    public CustomException(ExceptionType type, Object message) {
        super(type.getDesc() + message.toString());
        this.type = type;
    }
}

동작 흐름 예시

 

예를 들어, 사용자 조회 서비스에서 특정 사용자 ID를 찾을 수 없는 경우 예외를 발생시키는 상황을 가정해 봅시다.

// UserService.java
public User findUserById(Integer userId) {
    return userRepository.findById(userId)
            .orElseThrow(() -> new CustomException(ExceptionType.USER_NOT_FOUND, userId));
}

1. 예외 발생: findUserById 메서드에서 userRepository를 통해 ID로 사용자를 조회합니다. 사용자가 없으면 CustomException이 발생하여 ExceptionType.USER_NOT_FOUND가 전달됩니다.

2. 메시지 포함: ExceptionType.USER_NOT_FOUND가 포함된 예외 메시지와 추가 메시지 (userId)가 예외와 함께 반환됩니다. 최종 메시지는 유저가 데이터베이스 내 존재하지 않습니다. 유저 id : [userId]와 같습니다.

 

(따로 try catch로 잡지않고 RestController에서 오류 발생시)

3. ExceptionHandler 처리: 예외가 발생하여 CustomExceptionHandler로 전달되면, ExceptionType을 기반으로 로그와 응답을 생성합니다.

 

CustomExceptionHandler 활용 예시

 

CustomExceptionHandler를 통해 CustomException이 발생했을 때 일관된 형식의 응답을 반환할 수 있습니다.

@RestControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler
    public BaseResponse<Void> handle(CustomException e) {
        ExceptionType type = e.getType();
        log.atLevel(type.getLevel()).setCause(e).log(e.getMessage());
        return BaseResponse.failure(type);
    }

    @ExceptionHandler
    public BaseResponse<Void> handle(Exception e) {
        log.error(e.getMessage(), e);
        return BaseResponse.failure(ExceptionType.UNCLASSIFIED_ERROR);
    }
}

 

 

정리 요약

 

1. RuntimeException: 자바에서 실행 시 발생하는 unchecked 예외를 처리하기 위해 제공되는 기본 클래스입니다. 커스텀 예외 처리를 위해 RuntimeException을 상속하여 프로젝트에 맞는 예외 클래스를 만들 수 있습니다.

2. CustomException: RuntimeException을 상속한 커스텀 예외 클래스입니다. ExceptionType을 필드로 받아 예외 유형을 구체적으로 구분하며, 예외 상황에 대한 고유 코드, 메시지, HTTP 상태, 로그 레벨을 포함한 정보를 추가할 수 있습니다.

3. ExceptionType 열거형: 여러 예외 유형을 정의하고, 각각의 코드, 설명, HTTP 상태, 로그 레벨을 저장하는 열거형입니다. 예외 유형에 따라 예외를 구분하고 일관되게 처리할 수 있습니다.

4. CustomExceptionHandler: @RestControllerAdvice@ExceptionHandler를 통해 CustomException과 기타 예외를 중앙에서 관리하며, 예외 발생 시 BaseResponse 형식의 JSON 응답을 반환하여 클라이언트에게 일관된 에러 정보를 제공합니다.

 

이를 통해 코드의 가독성, 유지보수성, 일관성이 높아지고, 예외 상황에 대해 더욱 세부적이고 통제된 처리가 가능합니다. CustomExceptionHandler는 try-catch 구문 없이 예외가 발생했을 때 처리할 수 있는 장점이 있어 현업에서도 많이 사용되는 예외 처리 방식입니다.