예외 처리 최종편🔥

가언·2024년 7월 24일
post-thumbnail

호텔 룸 코드 <호출 스택 처리과정>

예외처리 끝장내자!

📖 복습

  • Unchecked: Runtime(Call stack) 구동
    - 링킹 흐름/호출 스택 안에서 어디에서든 처리를 하면 된다!
  • Checked: Compile시 구동
    - 예외 처리를 하지 않으면 컴파일 오류 발생
    해결 방안
    1) 예외터진 곳에서 직접 try catch로 처리
    2) throws를 사용해서 책임 전가 처리
    • 예외를 전달받은 곳에서 예외 처리를 해야만 함!
  • Handler: 자동으로 호출되는 메소드

그런데.. unchecked exception은 진짜 어디에서든 처리하면돼..? 진짜로?

아닛 정신차려! 우리에겐 세가지 선택지가 있어!
1. 앞서 실습한 에러 터진 곳에서 바로 처리-> 이건 checked exception에서는 필수로 해야해!바로 처리안하면 컴파일 에러 생기거등!!
2. controller에서 처리
3. global하게 처리

1번은 앞선 포스트에서 실습했으니깐 2번부터 배워보자!!


  • @ExceptionHandler

    : 예외가 터지면 자동으로 호출되는 메소드 at Controller(무조건 컨트롤러에서만 실행됨)
  • 예시
    @ExceptionHandler(value = RoomNotFoundException.class) //이 예외 발생하면 아래 메소드 실행할거야!
    public String catchRoomNotFoundException(RoomNotFoundException e){
        return e.getMessage();
    }
    • value가 없어도 실행가능: 인자 예외 클래스를 통해 스프링이 추측 가능하기 때문!
  • 궁금증1❓RoomController의 Request를 처리하다가 발생한 예외를 AccommodationController에서 @ExceptionHandler로 처리가능할까?
    직접 한번 해보자 ! Room controller코드를 Hotel controller코드로 이동만하고 실행했더니...

    오류발생..
    그렇다면 답은 ❌NO❌
  • 궁금증2❓RoomController랑 AccommodationController가 같은 예외를 같은 방법으로 처리한다면?
    Ex. NullPointerException -> "없음!"
    => 다른 Controller끼리는 @ExceptionHandler 공유 ❌

두 군데에서 같은 로직을 중복되게 짜야한다는 말이야...?
너무 비효율적인데, 개발자가 이걸 두고만 봤을리가 없징,,ㅎ

=> 아이디어: totalController를 만들어서 그 안에 넣자!
그렇다면 totalController역할을 하는건,,?


  • 바로 @ControllerAdvice

    : AOP가 없으면 일어날 수 없는 어노테이션
    : 모든 controller들의 공통 Exception 처리 클래스 생성 가능!!
    : 모든 컨트롤러에 예외가 발생하면 낚아챌거야!!라는 의미 가진 어노테이션
    이거면 다양한 컨트롤러에서 발생하는 중복된 에러를 처리할 수 있겠당!


    실행했더니 400번 오류가 뜨네 😭 뭐가 문제일까?

  • 해결: @RestControllerAdvice을 사용 @RestControllerAdvice=@ControllerAdvice+@ResponseBody
    (ResponseBody에노테이션은 반환을 뷰로 하지 않고, 데이터로 넘겨줄거야! 라는 의미)

그런데 왜 아직도 200번 OK로 오는거지..?! 거슬린다💥
200번이 아니라 오류니깐 400이나 404 응답 코드를 보내줬으면 좋겠엉!
이때 사용하는 어노테이션이 @ResponseStatus!

@RestControllerAdvice
public class GlobalExceptionHandler { //모든 컨트롤러에 예외가 발생하면 낚아챌거야!!
    @ExceptionHandler(value = RoomNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String catchRoomNotFoundException(RoomNotFoundException e){
        System.out.println(e.getClass());
        return "No Room!!!";
    }
}

🎉성공!!🎉

~~### 진짜 어디에서든 처리하면 된다고..?!

controller에서 처리

응답을 보내는 곳이 controller이기 때문에 에러처리+응답코드와 함께 보내는게 좋을듯
unchecked exception은 어차피 controller에서 처리하니깐 프론트엔드에게 돌려주기전에 한방에 모아서 처리하자

예외가 터지는 곳에서 처리

우회를 시켜주자 다른 방향을 제시해주자
~~

로그 찍기 in JAVA

slf4j 공식문서

  • logger
  • log level: 로그에도 단계가 있는데 warning, error, 큰 에러, 정보 제공성 로그 등
    시스템마다 로그의 목적이 다른 경우가 있음 따라서 레벨에 따라서 로그를 찍어줌
    그런데 레벨을 설정하면 다른 레벨이 뜨지 않음
    - Trace: 중요도가 매우 낮은 정보
    - Debug: 진행상황 체크(심각도, 중요도 낮은 정보)
    - info: 진행상황
    - warn: 잠재적으로 유해한 이벤트
    - error: 오류 이벤트(치명적인지 여부는 상관 없음)

예시

@RestControllerAdvice
@Component
public class GlobalExceptionHandler { //모든 컨트롤러에 예외가 발생하면 낚아챌거야!!
   private final org.slf4j.Logger logger=LoggerFactory.getLogger(GlobalExceptionHandler.class);

   @ExceptionHandler(value = RoomNotFoundException.class)
   @ResponseStatus(HttpStatus.NOT_FOUND)
   public String catchRoomNotFoundException(RoomNotFoundException e){
       logger.error("exception class: {}", e.getClass());
//        System.out.println(e.getClass());
       return "No Room!!!";
   }

}

  • 프로젝트 환경에서 로그 레벨 지정 가능
resorces/application.properties
#log level setting
logging.level.root=warn (ex. 로그 레벨: warn로 지정)
  • 서버 환경에서도 별도로 로그 레벨 지정 가능

    • 개발 서버: debug
    • 운영 서버: info설정 권장
  • 이번에는 @slf4j인터페이스를 사용해서 로그를 찍어보자

    엥 에러가 뜨네 왜 에러가 뜰까?
    아하! annotatioProcessor를 추가안해줬구나!
    그럼 annotationProcessor의 역할은? 컴파일시점에서 annotation을 읽어주는 역할이구나!

  • 결과

profile
@gari_guri

1개의 댓글

comment-user-thumbnail
2024년 7월 25일

잘읽고 가용 ~!

답글 달기