IT'S DO
article thumbnail
728x90
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class EventApiController {

    @GetMapping("/test")
    public String test(){
        throw new RuntimeException("Holy! Exception...");
    }
}

위 적용전

 

 

# GlobalExceptionHandler 적용 

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@RestControllerAdvice // 컨트롤러 전역에서 발생할 수 있는 예외를 잡아 Throw // @ControllerAdvice + @ResponseBody가 적용된 형태
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class) // 특정 클래스에서 발생할 수 있는 예외를 잡아 Throw // 일반적으로 @ExceptionHandler는 @ControllerAdive가 선언된 클래스에 포함된 메서드에 선언
    public String handleRuntimeException(final RuntimeException e) {
        log.error("handleRuntimeException : {}", e.getMessage());
        return e.getMessage();
    }

}

 

 

 

한줄로 나옴

 

다음은 Advanced REST Client에서 애플리케이션 내에 등록(매핑)되지 않은 URI를 호출한 결과입니다.

예외 응답은 이미지와 같이 일관성 있는 포맷을 가질 필요가 있습니다.

만약, API마다 예외에 대한 응답 포맷(Response Format)이 다르다면 어떻게 될까요?

예시를 한번 들어보도록 할게요.


개발자 A: "나는 status랑 message만 Response로 내려줄래."

개발자 B: "나는 status랑 error랑 message를 Response로 내려줄래."

개발자 C: "나는 timestamp랑 statu랑 error랑 message를 Response로 내려줄래.


네, 상상만 해도 끔찍한 상황이지요?

이렇게, 일관성이 흐트러지는 상황을 방지하기 위해서는

Response로 내려줄, 항상 동일한 구조의 응답 포맷을 미리 설계하는 게 가장 효율적이라고 생각하는데요,

글로는 이해가 쉽지 않으실 수 있으니, 직접 코드를 작성해 볼게요.

 

 


 

한 패키지 안에 

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class CustomException extends RuntimeException {

    private final ErrorCode errorCode;

}
import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ErrorCode {

    /*
     * 400 BAD_REQUEST: 잘못된 요청
     */
    BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."),

    /*
     * 404 NOT_FOUND: 리소스를 찾을 수 없음
     */
    POSTS_NOT_FOUND(HttpStatus.NOT_FOUND, "정보를 찾을 수 없습니다."),

    /*
     * 405 METHOD_NOT_ALLOWED: 허용되지 않은 Request Method 호출
     */
    METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "허용되지 않은 메서드입니다."),

    /*
     * 500 INTERNAL_SERVER_ERROR: 내부 서버 오류
     */
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "내부 서버 오류입니다."),

    ;

    private final HttpStatus status;
    private final String message;

}
import java.time.LocalDateTime;

import lombok.Getter;

@Getter
public class ErrorResponse {

    private final LocalDateTime timestamp = LocalDateTime.now();
    private final int status;
    private final String error;
    private final String code;
    private final String message;

    public ErrorResponse(ErrorCode errorCode) {
        this.status = errorCode.getStatus().value();
        this.error = errorCode.getStatus().name();
        this.code = errorCode.name();
        this.message = errorCode.getMessage();
    }

}
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /*
     * Developer Custom Exception
     */
    @ExceptionHandler(CustomException.class)
    protected ResponseEntity<ErrorResponse> handleCustomException(final CustomException e) {
        log.error("handleCustomException: {}", e.getErrorCode());
        return ResponseEntity
                .status(e.getErrorCode().getStatus().value())
                .body(new ErrorResponse(e.getErrorCode()));
    }

    /*
     * HTTP 405 Exception
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(final HttpRequestMethodNotSupportedException e) {
        log.error("handleHttpRequestMethodNotSupportedException: {}", e.getMessage());
        return ResponseEntity
                .status(ErrorCode.METHOD_NOT_ALLOWED.getStatus().value())
                .body(new ErrorResponse(ErrorCode.METHOD_NOT_ALLOWED));
    }

    /*
     * HTTP 500 Exception
     */
    @ExceptionHandler(Exception.class)
    protected ResponseEntity<ErrorResponse> handleException(final Exception e) {
        log.error("handleException: {}", e.getMessage());
        return ResponseEntity
                .status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus().value())
                .body(new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR));
    }

}

 

적용 후

 

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.dandy.history.exception.CustomException;
import com.example.dandy.history.exception.ErrorCode;

@RestController
@RequestMapping("/api")
public class EventApiController {

    @GetMapping("/test")
    public String test(){
//        throw new RuntimeException("Holy! Exception...");
        throw new CustomException(ErrorCode.POSTS_NOT_FOUND);
    }
}

 

 

 

 

 

좋은 사이트

 

너무 길어서 로그백은 다음 글에 씀

 

출처 : 

https://congsong.tistory.com/53?category=749196

profile

IT'S DO

@멋진놈

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!