자바 공부/스프링공부

Spring ExceptionHandler을 사용한 예외처리

ari0930 2025. 3. 16. 23:01

Spring ExceptionHandler을 사용한 예외처리

 

ExceptionHandler란?

스프링 부트에서 예외를 처리하는데 사용하는 어노테이션으로 특정 컨트롤러에서 발생하는 예외를 개별적으로 처리 하도록 하거나

전역적으로 관리하는데 활용됩니다.


 

내가 작성할거는 전역 예외 처리 방법이다.


전역 예외 처리

ControllerAdvice란?

개별 컨트롤러에서 처리하는 대신 전역적으로 예를 관리하도록 해주는 어노테이션으로

@ ControllerAdvice 와 @RestControllerAdvice 어노테이션이 존재한다.

  • @ ControllerAdvice : 일반 @ Controller 에서 view를 반환하는 컨트롤러에 대한 예외를 처리한다
  • @RestControllerAdvice : REST API 컨트롤러에 대한 예외를 처리한다.

구현 방법

1.사용자 정의 예외 클래스 만들기

 

public class HospitalException extends RuntimeException{
    private final ErrorCode errorCode;
    public HospitalException(ErrorCode errorCode, Exception e) {
        super(errorCode.getMessage(),e); // 메시지를 super로 전달
        this.errorCode = errorCode;
    }

    public HospitalException(ErrorCode errorCode) {
        super(errorCode.getMessage()); // 메시지를 super로 전달
        this.errorCode = errorCode;
    }

    public ErrorCode getErrorCode() {
        return errorCode;
    }

    public HttpStatus getStatus() {
        return errorCode.getStatus();
    }
}

 

여기 에러코드가 있는데 이건 내가 정의한 HTTP 상태코드로 

//2xx - 성공:
//OK (200)
//CREATED (201)
//NO_CONTENT (204)
//4xx - 클라이언트 오류:
//BAD_REQUEST (400)
//UNAUTHORIZED (401)
//FORBIDDEN (403)
//NOT_FOUND (404)
//5xx - 서버 오류:
//INTERNAL_SERVER_ERROR (500)
//SERVICE_UNAVAILABLE (503)
public enum ErrorCode {

    COORDINATE_NOT_KOREA("현재 위치가 한국이 아닙니다.", HttpStatus.NOT_FOUND),
    HOSPITAL_NOT_FOUND("병원을 찾을 수 없습니다.", HttpStatus.NOT_FOUND);
    private final String message;
    private final HttpStatus status;
    ErrorCode(String message, HttpStatus status) {
        this.message = message;
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    // 상태 코드 가져오기
    public HttpStatus getStatus() {
        return status;
    }
}

이렇게 에러코드들을 미리 정의하여 내가 원하는 상태값으로 리턴하고 어떠한 에로가 발생했는지 미리 지정할 수 있다.

 

2. 에러 응답 객체 정의

@Getter
@NoArgsConstructor //기본생성자 자동생성
public class ErrorResponse {
    private String message; //에러메시지
    private int status; //http 상태코드
    private LocalDateTime timestamp;

    public ErrorResponse(String s, int value, LocalDateTime now) {
        this.message=s;
        this.status=value;
        this.timestamp=now;
    }

    public ErrorResponse(ErrorCode errorCode, LocalDateTime timestamp) {
        this.message = errorCode.getMessage();
        this.status = errorCode.getStatus().value(); // HttpStatus의 value() 메서드를 통해 상태 코드 값 사용
        this.timestamp = timestamp;
    }
}

 

3.전역 예외 처리 클래스 구현

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(HospitalException.class)
    public ResponseEntity<ErrorResponse> HospitalException(HospitalException ex){
        ErrorCode errorCode = ex.getErrorCode();
        ErrorResponse errorResponse= new ErrorResponse(errorCode,
                LocalDateTime.now());

        log.error("병원컨트롤러에서 예외 발생 원인:{}",ex.getCause());


        return ResponseEntity.status(errorCode.getStatus()).body(errorResponse);

    }
    
    
}

 

이때  @ExceptionHandler(예외클래스명.class) 을 사용하여 내가 전역적으로 처리할 예외 를 직접 지정하여 처리 할 수 있다.

이렇게 구현할수 있으면 실제 적용 한다면

서비스

    public List<Hospital> getHospitals(double x,double y) throws HospitalException {
        double minLat=y-0.0053;
        double maxLat=y+0.0053;

        double minLon=x-0.0053;
        double maxLon=x+0.0053;
        if (122> x || x>133 || 30>y || y>39){
            throw new HospitalException(ErrorCode.COORDINATE_NOT_KOREA);
        }

        try {
            List<Hospital> hospitals=hospitalRepository.findHospitalsInRange(minLat,maxLat,minLon,maxLon);
        }catch (Exception e){
            throw new HospitalException(ErrorCode.HOSPITAL_NOT_FOUND,e);
        }

        return hospitalRepository.findHospitalsInRange(minLat,maxLat,minLon,maxLon);

    }

사용자 정의 예외 클래스와 , 내가 정의한 에러코드를 이용하여 서비스단에서 예외를 발생시키면

컨트롤러

    @GetMapping("/gethospitals")
    public ResponseEntity<List<Hospital>> getHospitals(@RequestParam double x, @RequestParam double y){
        log.debug("getHospitals 접근 x:{} y:{}",x,y);
        List<Hospital> hospitals=hospitalService.getHospitals(x,y);
        log.debug("주변 병원 count {}",hospitals.size());

        return ResponseEntity.ok(hospitals);

    }

이렇게 마루런 조치 없이도 서비스단에서 발생한 예외가 컨트롤러에게 넘어가면 자동적으로 

전역 예외 처리 클래스인 GlobalExceptionHandler 여기에서 맞는 예외를 찾아가 실행하여 클라이언트에게 응답을 전달한다.

반응형